Skip to content

Commit

Permalink
fix(core): remove extensions vec from asset
Browse files Browse the repository at this point in the history
Inheritence wasn't ever going to work, was it?
  • Loading branch information
gadomski committed Sep 19, 2024
1 parent 8dcd257 commit 3fa8a92
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 73 deletions.
2 changes: 2 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- `stac::geoparquet::Compression`, even if geoparquet is not enabled ([#396](https://github.com/stac-utils/stac-rs/pull/396))
- `Type` ([#397](https://github.com/stac-utils/stac-rs/pull/397))
- `Collection::item_assets` and `ItemAsset` ([#404](https://github.com/stac-utils/stac-rs/pull/404))
- A few extension methods on `Fields` ([#405](https://github.com/stac-utils/stac-rs/pull/405))

### Changed

Expand All @@ -26,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Removed

- `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))

## [0.9.0] - 2024-09-05

Expand Down
15 changes: 1 addition & 14 deletions core/src/asset.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Band, DataType, Extensions, Fields, Statistics};
use crate::{Band, DataType, Fields, Statistics};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::collections::HashMap;
Expand Down Expand Up @@ -80,9 +80,6 @@ pub struct Asset {
/// Additional fields on the asset.
#[serde(flatten)]
pub additional_fields: Map<String, Value>,

#[serde(skip)]
extensions: Vec<String>,
}

/// Trait implemented by anything that has assets.
Expand Down Expand Up @@ -141,7 +138,6 @@ impl Asset {
statistics: None,
unit: None,
additional_fields: Map::new(),
extensions: Vec::new(),
}
}

Expand Down Expand Up @@ -172,15 +168,6 @@ impl Fields for Asset {
}
}

impl Extensions for Asset {
fn extensions(&self) -> &Vec<String> {
&self.extensions
}
fn extensions_mut(&mut self) -> &mut Vec<String> {
&mut self.extensions
}
}

impl From<String> for Asset {
fn from(value: String) -> Self {
Asset::new(value)
Expand Down
10 changes: 3 additions & 7 deletions core/src/extensions/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,21 @@ impl Extension for Authentication {
#[cfg(test)]
mod tests {
use super::{Authentication, In, Scheme};
use crate::{Collection, Extensions, Item};
use crate::{Collection, Fields, Item};
use serde_json::json;

#[test]
fn collection() {
let collection: Collection = crate::read("data/auth/collection.json").unwrap();
let authentication: Authentication = collection.extension().unwrap().unwrap();
let authentication: Authentication = collection.extension().unwrap();
let oauth = authentication.schemes.get("oauth").unwrap();
let _ = oauth.flows.get("authorizationCode").unwrap();
// FIXME: assets should be able to have extensions from their parent item
// let asset = collection.assets.get("example").unwrap();
// let authentication: Authentication = asset.extension().unwrap().unwrap();
// assert_eq!(authentication.refs, vec!["signed_url_auth".to_string()]);
}

#[test]
fn item() {
let collection: Item = crate::read("data/auth/item.json").unwrap();
let authentication: Authentication = collection.extension().unwrap().unwrap();
let authentication: Authentication = collection.extension().unwrap();
let _ = authentication.schemes.get("none").unwrap();
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/extensions/electro_optical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ impl Extension for ElectroOptical {
#[cfg(test)]
mod tests {
use super::ElectroOptical;
use crate::{Extensions, Item};
use crate::{Fields, Item};

#[test]
fn item() {
let item: Item = crate::read("data/eo/item.json").unwrap();
let _: ElectroOptical = item.extension().unwrap().unwrap();
let _: ElectroOptical = item.extension().unwrap();
}
}
30 changes: 4 additions & 26 deletions core/src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,6 @@ pub trait Extensions: Fields {
.any(|extension| extension.starts_with(E::identifier_prefix()))
}

/// Gets an extension's data.
///
/// Returns `Ok(None)` if the object doesn't have the given extension.
///
/// # Examples
///
/// ```
/// use stac::{Item, extensions::{Projection, Extensions}};
/// let item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
/// let projection: Projection = item.extension().unwrap().unwrap();
/// assert_eq!(projection.code.unwrap(), "EPSG:32614");
/// ```
fn extension<E: Extension>(&self) -> Result<Option<E>> {
if self.has_extension::<E>() {
self.fields_with_prefix(E::PREFIX).map(|v| Some(v))
} else {
Ok(None)
}
}

/// Adds an extension's identifier to this object.
///
/// # Examples
Expand Down Expand Up @@ -169,10 +149,9 @@ pub trait Extensions: Fields {
/// item.set_extension(projection).unwrap();
/// ```
fn set_extension<E: Extension>(&mut self, extension: E) -> Result<()> {
self.remove_extension::<E>();
self.extensions_mut().push(E::IDENTIFIER.to_string());
self.extensions_mut().dedup();
self.set_fields_with_prefix(E::PREFIX, extension)
Fields::set_extension(self, extension)
}

/// Removes this extension and all of its fields from this object.
Expand All @@ -187,8 +166,7 @@ pub trait Extensions: Fields {
/// assert!(!item.has_extension::<Projection>());
/// ```
fn remove_extension<E: Extension>(&mut self) {
// TODO how do we handle removing from assets when this is done on an item?
self.remove_fields_with_prefix(E::PREFIX);
Fields::remove_extension::<E>(self);
self.extensions_mut()
.retain(|extension| !extension.starts_with(E::identifier_prefix()))
}
Expand Down Expand Up @@ -220,13 +198,13 @@ mod tests {

#[test]
fn set_extension_on_asset() {
use crate::Fields;

let mut asset = Asset::new("a/href.tif");
assert!(!asset.has_extension::<Raster>());
let mut band = Band::default();
band.unit = Some("parsecs".to_string());
let raster = Raster { bands: vec![band] };
asset.set_extension(raster).unwrap();
assert!(asset.has_extension::<Raster>());
let mut item = Item::new("an-id");
let _ = item.assets.insert("data".to_string(), asset);
}
Expand Down
20 changes: 18 additions & 2 deletions core/src/extensions/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ impl Projection {
Ok(None)
}
}

/// Returns true if this projection structure is empty.
///
/// # Examples
///
/// ```
/// use stac::extensions::Projection;
///
/// let projection = Projection::default();
/// assert!(projection.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
serde_json::to_value(self)
.map(|v| v == Value::Object(Default::default()))
.unwrap_or(true)
}
}

impl Extension for Projection {
Expand All @@ -134,7 +150,7 @@ impl Extension for Projection {
#[cfg(test)]
mod tests {
use super::Projection;
use crate::{Extensions, Item};
use crate::{Fields, Item};

#[cfg(feature = "gdal")]
#[test]
Expand Down Expand Up @@ -162,7 +178,7 @@ mod tests {
fn example() {
let item: Item =
crate::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
let projection = item.extension::<Projection>().unwrap().unwrap();
let projection = item.extension::<Projection>().unwrap();
assert_eq!(projection.code.unwrap(), "EPSG:32614");
}
}
18 changes: 18 additions & 0 deletions core/src/extensions/raster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ impl Extension for Raster {
const PREFIX: &'static str = "raster";
}

impl Raster {
/// Returns true if this raster structure is empty.
///
/// # Examples
///
/// ```
/// use stac::extensions::Raster;
///
/// let projection = Raster::default();
/// assert!(projection.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
serde_json::to_value(self)
.map(|v| v == serde_json::Value::Object(Default::default()))
.unwrap_or(true)
}
}

#[cfg(feature = "gdal")]
impl From<gdal::raster::GdalDataType> for DataType {
fn from(value: gdal::raster::GdalDataType) -> Self {
Expand Down
52 changes: 50 additions & 2 deletions core/src/fields.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Error, Result};
use crate::{Error, Extension, Result};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{json, Map, Value};

Expand Down Expand Up @@ -124,11 +124,59 @@ pub trait Fields {
/// use stac::{Fields, Item, extensions::Projection};
/// let projection = Projection { code: Some("EPSG:4326".to_string()), ..Default::default() };
/// let mut item = Item::new("an-id");
/// item.remove_fields_with_prefix("proj"); // Prefer `Extensions::remove_extension`
/// item.remove_fields_with_prefix("proj"); // Prefer `Fields::remove_extension`
/// ```
fn remove_fields_with_prefix(&mut self, prefix: &str) {
let prefix = format!("{}:", prefix);
self.fields_mut()
.retain(|key, _| !(key.starts_with(&prefix) && key.len() > prefix.len()));
}

/// Gets an extension's data.
///
/// Returns `Ok(None)` if the object doesn't have the given extension.
///
/// # Examples
///
/// ```
/// use stac::{Item, Fields, extensions::Projection};
/// let item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
/// let projection: Projection = item.extension().unwrap();
/// assert_eq!(projection.code.unwrap(), "EPSG:32614");
/// ```
fn extension<E: Extension>(&self) -> Result<E> {
self.fields_with_prefix(E::PREFIX)
}

/// Sets an extension's data into this object.
///
/// This will remove any previous fields from this extension
///
/// # Examples
///
/// ```
/// use stac::{Item, Fields, extensions::Projection};
/// let mut item = Item::new("an-id");
/// let projection = Projection { code: Some("EPSG:4326".to_string()), ..Default::default() };
/// item.set_extension(projection).unwrap();
/// ```
fn set_extension<E: Extension>(&mut self, extension: E) -> Result<()> {
self.remove_extension::<E>();
self.set_fields_with_prefix(E::PREFIX, extension)
}

/// Removes all of the extension's fields from this object.
///
/// # Examples
///
/// ```
/// use stac::{Item, extensions::{Projection, Extensions}};
/// let mut item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
/// assert!(item.has_extension::<Projection>());
/// item.remove_extension::<Projection>();
/// assert!(!item.has_extension::<Projection>());
/// ```
fn remove_extension<E: Extension>(&mut self) {
self.remove_fields_with_prefix(E::PREFIX);
}
}
29 changes: 9 additions & 20 deletions core/src/gdal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
raster::{Band, Raster, Statistics},
Projection,
},
Asset, Bbox, Extensions, Item, Result,
Asset, Bbox, Extensions, Fields, Item, Result,
};
use gdal::{
spatial_ref::{CoordTransform, SpatialRef},
Expand Down Expand Up @@ -44,14 +44,15 @@ pub fn update_item(
let mut bbox = Bbox::new(180., 90., -180., 90.); // Intentionally invalid bbox so the first update always takes
for asset in item.assets.values_mut() {
update_asset(asset, force_statistics, is_approx_statistics_ok)?;
if let Some(projection) = asset.extension::<Projection>()? {
let projection = asset.extension::<Projection>()?;
if !projection.is_empty() {
has_projection = true;
if let Some(asset_bounds) = projection.wgs84_bounds()? {
bbox.update(asset_bounds);
}
projections.push(projection);
}
if !has_raster && asset.has_extension::<Raster>() {
if !has_raster && !asset.extension::<Raster>()?.is_empty() {
has_raster = true;
}
}
Expand All @@ -65,7 +66,7 @@ pub fn update_item(
.iter()
.all(|projection| *projection == projections[0])
{
item.set_extension(projections[0].clone())?;
Extensions::set_extension(item, projections[0].clone())?;
for asset in item.assets.values_mut() {
asset.remove_extension::<Projection>();
}
Expand Down Expand Up @@ -174,7 +175,7 @@ mod tests {
use crate::{
extensions::{projection::Centroid, raster::DataType, Projection, Raster},
item::Builder,
Extensions,
Extensions, Fields,
};

#[test]
Expand All @@ -185,13 +186,7 @@ mod tests {
.unwrap();
super::update_item(&mut item, false, true).unwrap();
assert!(item.has_extension::<Raster>());
let raster: Raster = item
.assets
.get("data")
.unwrap()
.extension()
.unwrap()
.unwrap();
let raster: Raster = item.assets.get("data").unwrap().extension().unwrap();
assert_eq!(
*raster.bands[0].data_type.as_ref().unwrap(),
DataType::UInt16
Expand All @@ -205,13 +200,7 @@ mod tests {
.build()
.unwrap();
super::update_item(&mut item, false, true).unwrap();
let raster: Raster = item
.assets
.get("data")
.unwrap()
.extension()
.unwrap()
.unwrap();
let raster: Raster = item.assets.get("data").unwrap().extension().unwrap();
assert_eq!(
raster.bands[0].spatial_resolution.unwrap(),
100.01126757344893
Expand All @@ -225,7 +214,7 @@ mod tests {
.build()
.unwrap();
super::update_item(&mut item, false, true).unwrap();
let projection: Projection = item.extension().unwrap().unwrap();
let projection: Projection = item.extension().unwrap();
assert_eq!(projection.code.unwrap(), "EPSG:32621");
assert_eq!(
projection.bbox.unwrap(),
Expand Down

0 comments on commit 3fa8a92

Please sign in to comment.