Skip to content

Commit 5ff0a4e

Browse files
authored
feat: add tree structure (#509)
That was a journey, but we got there.
1 parent 8f1a6a7 commit 5ff0a4e

File tree

26 files changed

+754
-383
lines changed

26 files changed

+754
-383
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
name: Test stac
2020
runs-on: ${{ matrix.os }}
2121
strategy:
22+
fail-fast: false
2223
matrix:
2324
os:
2425
- ubuntu-latest
@@ -28,7 +29,7 @@ jobs:
2829
- uses: actions/checkout@v4
2930
- uses: Swatinem/rust-cache@v2
3031
- name: Test
31-
run: cargo test -p stac --all-features
32+
run: cargo test -p stac -p stac-types --all-features
3233
check-features-core:
3334
name: Check stac features
3435
runs-on: ubuntu-latest

crates/api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ client = [
1818
"dep:http",
1919
"dep:reqwest",
2020
"dep:tokio",
21+
"stac-types/reqwest",
2122
]
2223
geo = ["dep:geo", "stac/geo"]
2324

crates/api/src/client.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use http::header::{HeaderName, USER_AGENT};
77
use reqwest::{header::HeaderMap, ClientBuilder, IntoUrl, Method, StatusCode};
88
use serde::{de::DeserializeOwned, Serialize};
99
use serde_json::{Map, Value};
10-
use stac::{Collection, Href, Link, Links};
10+
use stac::{Collection, Link, Links, SelfHref};
1111
use std::pin::Pin;
1212
use tokio::{
1313
runtime::{Builder, Runtime},
@@ -170,13 +170,13 @@ impl Client {
170170

171171
async fn get<V>(&self, url: impl IntoUrl) -> Result<V>
172172
where
173-
V: DeserializeOwned + Href,
173+
V: DeserializeOwned + SelfHref,
174174
{
175175
let url = url.into_url()?;
176176
let mut value = self
177177
.request::<(), V>(Method::GET, url.clone(), None, None)
178178
.await?;
179-
value.set_href(url);
179+
*value.self_href_mut() = Some(url.into());
180180
Ok(value)
181181
}
182182

@@ -243,7 +243,7 @@ impl Client {
243243
} else {
244244
None
245245
};
246-
self.request::<Map<String, Value>, R>(method, link.href, &link.body, headers)
246+
self.request::<Map<String, Value>, R>(method, link.href.as_str(), &link.body, headers)
247247
.await
248248
}
249249
}
@@ -404,7 +404,7 @@ mod tests {
404404
let mut page_1_body: ItemCollection =
405405
serde_json::from_str(include_str!("../mocks/search-page-1.json")).unwrap();
406406
let mut next_link = page_1_body.link("next").unwrap().clone();
407-
next_link.href = format!("{}/search", server.url());
407+
next_link.href = format!("{}/search", server.url()).into();
408408
page_1_body.set_link(next_link);
409409
let page_1 = server
410410
.mock("POST", "/search")
@@ -454,13 +454,14 @@ mod tests {
454454
let mut page_1_body: ItemCollection =
455455
serde_json::from_str(include_str!("../mocks/items-page-1.json")).unwrap();
456456
let mut next_link = page_1_body.link("next").unwrap().clone();
457-
let url: Url = next_link.href.parse().unwrap();
457+
let url: Url = next_link.href.as_str().parse().unwrap();
458458
let query = url.query().unwrap();
459459
next_link.href = format!(
460460
"{}/collections/sentinel-2-l2a/items?{}",
461461
server.url(),
462462
query
463-
);
463+
)
464+
.into();
464465
page_1_body.set_link(next_link);
465466
let page_1 = server
466467
.mock("GET", "/collections/sentinel-2-l2a/items?limit=1")
@@ -500,13 +501,14 @@ mod tests {
500501
let mut page_body: ItemCollection =
501502
serde_json::from_str(include_str!("../mocks/items-page-1.json")).unwrap();
502503
let mut next_link = page_body.link("next").unwrap().clone();
503-
let url: Url = next_link.href.parse().unwrap();
504+
let url: Url = next_link.href.as_str().parse().unwrap();
504505
let query = url.query().unwrap();
505506
next_link.href = format!(
506507
"{}/collections/sentinel-2-l2a/items?{}",
507508
server.url(),
508509
query
509-
);
510+
)
511+
.into();
510512
page_body.set_link(next_link);
511513
page_body.items = vec![];
512514
let page = server

crates/api/src/collections.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use serde::{Deserialize, Serialize};
22
use serde_json::{Map, Value};
3-
use stac::{Collection, Link};
4-
use stac_derive::{Href, Links};
3+
use stac::{Collection, Href, Link};
4+
use stac_derive::{Links, SelfHref};
55

66
/// Object containing an array of collections and an array of links.
7-
#[derive(Debug, Serialize, Deserialize, Href, Links)]
7+
#[derive(Debug, Serialize, Deserialize, SelfHref, Links)]
88
pub struct Collections {
99
/// The [Collection] objects in the [stac::Catalog].
1010
pub collections: Vec<Collection>,
@@ -17,7 +17,7 @@ pub struct Collections {
1717
pub additional_fields: Map<String, Value>,
1818

1919
#[serde(skip)]
20-
href: Option<String>,
20+
self_href: Option<Href>,
2121
}
2222

2323
impl From<Vec<Collection>> for Collections {
@@ -26,7 +26,7 @@ impl From<Vec<Collection>> for Collections {
2626
collections,
2727
links: Vec::new(),
2828
additional_fields: Map::new(),
29-
href: None,
29+
self_href: None,
3030
}
3131
}
3232
}

crates/api/src/item_collection.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use crate::{Item, Result};
22
use serde::{Deserialize, Serialize};
33
use serde_json::{Map, Value};
4-
use stac::Link;
5-
use stac_derive::{Href, Links};
4+
use stac::{Href, Link};
5+
use stac_derive::{Links, SelfHref};
66

77
/// The return value of the `/items` and `/search` endpoints.
88
///
99
/// This might be a [stac::ItemCollection], but if the [fields
1010
/// extension](https://github.com/stac-api-extensions/fields) is used, it might
1111
/// not be. Defined by the [itemcollection
1212
/// fragment](https://github.com/radiantearth/stac-api-spec/blob/main/fragments/itemcollection/README.md).
13-
#[derive(Debug, Serialize, Deserialize, Default, Links, Href)]
13+
#[derive(Debug, Serialize, Deserialize, Default, Links, SelfHref)]
1414
#[serde(tag = "type", rename = "FeatureCollection")]
1515
pub struct ItemCollection {
1616
/// A possibly-empty array of Item objects.
@@ -67,7 +67,7 @@ pub struct ItemCollection {
6767
pub last: Option<Map<String, Value>>,
6868

6969
#[serde(skip)]
70-
href: Option<String>,
70+
self_href: Option<Href>,
7171
}
7272

7373
/// The search-related metadata for the [ItemCollection].
@@ -114,7 +114,7 @@ impl ItemCollection {
114114
prev: None,
115115
first: None,
116116
last: None,
117-
href: None,
117+
self_href: None,
118118
})
119119
}
120120
}

crates/cli/src/subcommand/serve.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl Run for Args {
9393
}
9494
Value::Collection(mut collection) => {
9595
if self.load_collection_items {
96-
collection.make_relative_links_absolute()?;
96+
collection.make_links_absolute()?;
9797
for link in collection.iter_item_links() {
9898
let href = link.href.to_string();
9999
let input = input.with_href(href);

crates/core/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
99
### Added
1010

1111
- `version` ([#476](https://github.com/stac-utils/stac-rs/pull/476))
12+
- `Node` and friends ([#504](https://github.com/stac-utils/stac-rs/pull/504))
13+
14+
### Changed
15+
16+
- `make_links_absolute` instead of `make_relative_links_absolute`, `make_links_relative` instead of `make_absolute_links_relative` ([#504](https://github.com/stac-utils/stac-rs/pull/504))
1217
- Permissive deserialization ([#505](https://github.com/stac-utils/stac-rs/pull/505))
1318

1419
### Removed

crates/core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object-store-all = [
4242
"object-store-gcp",
4343
"object-store-http",
4444
]
45-
reqwest = ["dep:reqwest"]
45+
reqwest = ["dep:reqwest", "stac-types/reqwest"]
4646
reqwest-rustls = ["reqwest/rustls-tls"]
4747
validate = ["dep:jsonschema", "dep:reqwest", "dep:tokio", "dep:fluent-uri"]
4848
validate-blocking = ["validate", "tokio/rt"]

crates/core/src/catalog.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::{Error, Link, Result, Version, STAC_VERSION};
1+
use crate::{Error, Href, Link, Result, Version, STAC_VERSION};
22
use serde::{Deserialize, Serialize};
33
use serde_json::{Map, Value};
4-
use stac_derive::{Fields, Href, Links, Migrate};
4+
use stac_derive::{Fields, Links, Migrate, SelfHref};
55

66
/// A STAC Catalog object represents a logical group of other `Catalog`,
77
/// [Collection](crate::Collection), and [Item](crate::Item) objects.
@@ -15,7 +15,7 @@ use stac_derive::{Fields, Href, Links, Migrate};
1515
/// A `Catalog` object will typically be the entry point into a STAC catalog.
1616
/// Their purpose is discovery: to be browsed by people or be crawled by clients
1717
/// to build a searchable index.
18-
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Href, Migrate, Links, Fields)]
18+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, SelfHref, Migrate, Links, Fields)]
1919
#[serde(tag = "type")]
2020
pub struct Catalog {
2121
/// The STAC version the `Catalog` implements.
@@ -51,7 +51,7 @@ pub struct Catalog {
5151
pub additional_fields: Map<String, Value>,
5252

5353
#[serde(skip)]
54-
href: Option<String>,
54+
self_href: Option<Href>,
5555
}
5656

5757
impl Catalog {
@@ -74,7 +74,7 @@ impl Catalog {
7474
description: description.to_string(),
7575
links: Vec::new(),
7676
additional_fields: Map::new(),
77-
href: None,
77+
self_href: None,
7878
}
7979
}
8080
}

crates/core/src/collection.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::{
2-
Asset, Assets, Bbox, Error, Href, Item, ItemAsset, Link, Links, Migrate, Result, Version,
3-
STAC_VERSION,
2+
Asset, Assets, Bbox, Error, Href, Item, ItemAsset, Link, Links, Migrate, Result, SelfHref,
3+
Version, STAC_VERSION,
44
};
55
use chrono::{DateTime, Utc};
66
use serde::{Deserialize, Serialize};
77
use serde_json::{Map, Value};
8-
use stac_derive::{Fields, Href, Links};
8+
use stac_derive::{Fields, Links, SelfHref};
99
use std::collections::HashMap;
1010

1111
const DEFAULT_LICENSE: &str = "proprietary";
@@ -22,7 +22,7 @@ const DEFAULT_LICENSE: &str = "proprietary";
2222
/// A STAC `Collection` is represented in JSON format. Any JSON object that
2323
/// contains all the required fields is a valid STAC `Collection` and also a valid
2424
/// STAC `Catalog`.
25-
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Href, Links, Fields)]
25+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, SelfHref, Links, Fields)]
2626
#[serde(tag = "type")]
2727
pub struct Collection {
2828
/// The STAC version the `Collection` implements.
@@ -93,7 +93,7 @@ pub struct Collection {
9393
pub additional_fields: Map<String, Value>,
9494

9595
#[serde(skip)]
96-
href: Option<String>,
96+
self_href: Option<Href>,
9797
}
9898

9999
/// This object provides information about a provider.
@@ -184,7 +184,7 @@ impl Collection {
184184
assets: HashMap::new(),
185185
item_assets: HashMap::new(),
186186
additional_fields: Map::new(),
187-
href: None,
187+
self_href: None,
188188
}
189189
}
190190

@@ -249,11 +249,8 @@ impl Collection {
249249
}
250250

251251
fn maybe_add_item_link(&mut self, item: &Item) -> Option<&Link> {
252-
if let Some(href) = item
253-
.href()
254-
.or(item.self_link().map(|link| link.href.as_str()))
255-
{
256-
self.links.push(Link::item(href));
252+
if let Some(href) = item.self_href().or(item.self_link().map(|link| &link.href)) {
253+
self.links.push(Link::item(href.clone()));
257254
self.links.last()
258255
} else {
259256
None
@@ -448,7 +445,7 @@ mod tests {
448445
.unwrap()
449446
);
450447
let link = collection.link("item").unwrap();
451-
assert_eq!(link.href, "examples/simple-item.json");
448+
assert!(link.href.to_string().ends_with("simple-item.json"));
452449
}
453450
}
454451

0 commit comments

Comments
 (0)