diff --git a/api/src/collections.rs b/api/src/collections.rs index 96cddd58..f5116d6b 100644 --- a/api/src/collections.rs +++ b/api/src/collections.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use stac::{Collection, Link, Links}; +use stac::{Collection, Href, Link, Links}; /// Object containing an array of collections and an array of links. #[derive(Debug, Serialize, Deserialize)] @@ -14,6 +14,9 @@ pub struct Collections { /// Additional fields. #[serde(flatten)] pub additional_fields: Map, + + #[serde(skip)] + href: Option, } impl From> for Collections { @@ -22,10 +25,23 @@ impl From> for Collections { collections, links: Vec::new(), additional_fields: Map::new(), + href: None, } } } +impl Href for Collections { + fn href(&self) -> Option<&str> { + self.href.as_deref() + } + fn set_href(&mut self, href: impl ToString) { + self.href = Some(href.to_string()); + } + fn clear_href(&mut self) { + self.href = None; + } +} + impl Links for Collections { fn links(&self) -> &[Link] { &self.links diff --git a/api/src/item_collection.rs b/api/src/item_collection.rs index d757fdd9..3e4b7980 100644 --- a/api/src/item_collection.rs +++ b/api/src/item_collection.rs @@ -1,7 +1,7 @@ use crate::{Item, Result}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use stac::{Link, Links}; +use stac::{Href, Link, Links}; const ITEM_COLLECTION_TYPE: &str = "FeatureCollection"; @@ -71,6 +71,9 @@ pub struct ItemCollection { /// pagination information (tokens) to later be turned into links. #[serde(skip)] pub last: Option>, + + #[serde(skip)] + href: Option, } /// The search-related metadata for the [ItemCollection]. @@ -118,10 +121,23 @@ impl ItemCollection { prev: None, first: None, last: None, + href: None, }) } } +impl Href for ItemCollection { + fn set_href(&mut self, href: impl ToString) { + self.href = Some(href.to_string()); + } + fn clear_href(&mut self) { + self.href = None; + } + fn href(&self) -> Option<&str> { + self.href.as_deref() + } +} + impl Links for ItemCollection { fn links(&self) -> &[Link] { &self.links @@ -145,6 +161,7 @@ impl Default for ItemCollection { prev: None, first: None, last: None, + href: None, } } } diff --git a/cli/src/args/serve.rs b/cli/src/args/serve.rs index b2d03a8f..9f7e769e 100644 --- a/cli/src/args/serve.rs +++ b/cli/src/args/serve.rs @@ -1,6 +1,6 @@ use super::{Input, Run}; use crate::{Error, Result, Value}; -use stac::{Collection, Href, Item, Links}; +use stac::{Collection, Item, Links}; use stac_server::{Api, Backend as _, MemoryBackend}; use std::collections::{HashMap, HashSet}; use tokio::{net::TcpListener, sync::mpsc::Sender, task::JoinSet}; @@ -94,8 +94,7 @@ impl Run for Args { } Value::Collection(mut collection) => { if self.load_collection_items { - let href = collection.href().expect("we just read it").to_string(); - collection.make_relative_links_absolute(href)?; + collection.make_relative_links_absolute()?; for link in collection.iter_item_links() { let href = link.href.to_string(); let input = input.with_href(href); diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 7592a189..e61b8ad0 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `Error::ReqwestNotEnabled` and `Error::GdalNotEnabled` ([#396](https://github.com/stac-utils/stac-rs/pull/382)) - `Asset::extensions` ([#405](https://github.com/stac-utils/stac-rs/pull/405)) +- `href` argument to `Links::make_relative_links_absolute` ([#407](https://github.com/stac-utils/stac-rs/pull/407)) ## [0.9.0] - 2024-09-05 diff --git a/core/src/error.rs b/core/src/error.rs index aad29c2c..2456d5b4 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -83,6 +83,10 @@ pub enum Error { #[error("no items")] NoItems, + /// There is not an href, when an href is required. + #[error("no href")] + NoHref, + /// This value is not an item. #[error("value is not an item")] NotAnItem(Value), diff --git a/core/src/link.rs b/core/src/link.rs index f0719f2f..b5c229be 100644 --- a/core/src/link.rs +++ b/core/src/link.rs @@ -1,6 +1,6 @@ //! Links. -use crate::{mime::APPLICATION_GEOJSON, Error, Result}; +use crate::{mime::APPLICATION_GEOJSON, Error, Href, Result}; use mime::APPLICATION_JSON; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; @@ -88,7 +88,7 @@ pub struct Link { } /// Implemented by any object that has links. -pub trait Links { +pub trait Links: Href { /// Returns a reference to this object's links. /// /// # Examples @@ -217,7 +217,7 @@ pub trait Links { Box::new(self.links().iter().filter(|link| link.is_item())) } - /// Makes all relative links absolute with respect to an href. + /// Makes all relative links absolute with respect to this object's href. /// /// # Examples /// @@ -226,15 +226,19 @@ pub trait Links { /// /// let mut catalog: stac::Catalog = stac::read("examples/catalog.json").unwrap(); /// assert!(!catalog.root_link().unwrap().is_absolute()); - /// catalog.make_relative_links_absolute("examples/catalog.json").unwrap(); + /// catalog.make_relative_links_absolute().unwrap(); /// assert!(catalog.root_link().unwrap().is_absolute()); /// ``` - fn make_relative_links_absolute(&mut self, href: impl ToString) -> Result<()> { - let href = make_absolute(href.to_string(), None)?; - for link in self.links_mut() { - link.href = make_absolute(std::mem::take(&mut link.href), Some(&href))?; + fn make_relative_links_absolute(&mut self) -> Result<()> { + if let Some(href) = self.href() { + let href = make_absolute(href.to_string(), None)?; + for link in self.links_mut() { + link.href = make_absolute(std::mem::take(&mut link.href), Some(&href))?; + } + Ok(()) + } else { + Err(Error::NoHref) } - Ok(()) } /// Makes all absolute links relative with respect to an href. @@ -248,7 +252,7 @@ pub trait Links { /// /// let mut catalog: stac::Catalog = stac::read("examples/catalog.json").unwrap(); /// assert!(!catalog.root_link().unwrap().is_absolute()); - /// catalog.make_relative_links_absolute("examples/catalog.json").unwrap(); + /// catalog.make_relative_links_absolute().unwrap(); /// assert!(catalog.root_link().unwrap().is_absolute()); /// catalog.make_absolute_links_relative("examples/catalog.json").unwrap(); /// assert!(catalog.root_link().unwrap().is_relative()); @@ -817,7 +821,7 @@ mod tests { } mod links { - use crate::{Catalog, Item, Link, Links}; + use crate::{Catalog, Href, Item, Link, Links}; #[test] fn link() { @@ -846,9 +850,7 @@ mod tests { #[test] fn make_relative_links_absolute_path() { let mut catalog: Catalog = crate::read("examples/catalog.json").unwrap(); - catalog - .make_relative_links_absolute("examples/catalog.json") - .unwrap(); + catalog.make_relative_links_absolute().unwrap(); for link in catalog.links() { assert!(link.is_absolute()); } @@ -857,9 +859,8 @@ mod tests { #[test] fn make_relative_links_absolute_url() { let mut catalog: Catalog = crate::read("examples/catalog.json").unwrap(); - catalog - .make_relative_links_absolute("http://stac-rs.test/catalog.json") - .unwrap(); + catalog.set_href("http://stac-rs.test/catalog.json"); + catalog.make_relative_links_absolute().unwrap(); for link in catalog.links() { assert!(link.is_absolute()); } @@ -872,9 +873,7 @@ mod tests { #[test] fn make_absolute_links_relative_path() { let mut catalog: Catalog = crate::read("examples/catalog.json").unwrap(); - catalog - .make_relative_links_absolute("examples/catalog.json") - .unwrap(); + catalog.make_relative_links_absolute().unwrap(); catalog.make_absolute_links_relative("examples/").unwrap(); for link in catalog.links() { if !link.is_self() { @@ -886,9 +885,8 @@ mod tests { #[test] fn make_absolute_links_relative_url() { let mut catalog: Catalog = crate::read("examples/catalog.json").unwrap(); - catalog - .make_relative_links_absolute("http://stac-rs.test/catalog.json") - .unwrap(); + catalog.set_href("http://stac-rs.test/catalog.json"); + catalog.make_relative_links_absolute().unwrap(); catalog .make_absolute_links_relative("http://stac-rs.test/") .unwrap();