Skip to content

Commit f19d44b

Browse files
committed
fix(core): remove extensions vec from asset
Inheritence wasn't ever going to work, was it?
1 parent 8dcd257 commit f19d44b

File tree

9 files changed

+111
-79
lines changed

9 files changed

+111
-79
lines changed

core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1717
- `stac::geoparquet::Compression`, even if geoparquet is not enabled ([#396](https://github.com/stac-utils/stac-rs/pull/396))
1818
- `Type` ([#397](https://github.com/stac-utils/stac-rs/pull/397))
1919
- `Collection::item_assets` and `ItemAsset` ([#404](https://github.com/stac-utils/stac-rs/pull/404))
20+
- A few extension methods on `Fields` ([#405](https://github.com/stac-utils/stac-rs/pull/405))
2021

2122
### Changed
2223

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

2829
- `Error::ReqwestNotEnabled` and `Error::GdalNotEnabled` ([#396](https://github.com/stac-utils/stac-rs/pull/382))
30+
- `Asset::extensions` ([#405](https://github.com/stac-utils/stac-rs/pull/405))
2931

3032
## [0.9.0] - 2024-09-05
3133

core/src/asset.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Band, DataType, Extensions, Fields, Statistics};
1+
use crate::{Band, DataType, Fields, Statistics};
22
use serde::{Deserialize, Serialize};
33
use serde_json::{Map, Value};
44
use std::collections::HashMap;
@@ -80,9 +80,6 @@ pub struct Asset {
8080
/// Additional fields on the asset.
8181
#[serde(flatten)]
8282
pub additional_fields: Map<String, Value>,
83-
84-
#[serde(skip)]
85-
extensions: Vec<String>,
8683
}
8784

8885
/// Trait implemented by anything that has assets.
@@ -141,7 +138,6 @@ impl Asset {
141138
statistics: None,
142139
unit: None,
143140
additional_fields: Map::new(),
144-
extensions: Vec::new(),
145141
}
146142
}
147143

@@ -172,15 +168,6 @@ impl Fields for Asset {
172168
}
173169
}
174170

175-
impl Extensions for Asset {
176-
fn extensions(&self) -> &Vec<String> {
177-
&self.extensions
178-
}
179-
fn extensions_mut(&mut self) -> &mut Vec<String> {
180-
&mut self.extensions
181-
}
182-
}
183-
184171
impl From<String> for Asset {
185172
fn from(value: String) -> Self {
186173
Asset::new(value)

core/src/extensions/authentication.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,25 +167,21 @@ impl Extension for Authentication {
167167
#[cfg(test)]
168168
mod tests {
169169
use super::{Authentication, In, Scheme};
170-
use crate::{Collection, Extensions, Item};
170+
use crate::{Collection, Fields, Item};
171171
use serde_json::json;
172172

173173
#[test]
174174
fn collection() {
175175
let collection: Collection = crate::read("data/auth/collection.json").unwrap();
176-
let authentication: Authentication = collection.extension().unwrap().unwrap();
176+
let authentication: Authentication = collection.extension().unwrap();
177177
let oauth = authentication.schemes.get("oauth").unwrap();
178178
let _ = oauth.flows.get("authorizationCode").unwrap();
179-
// FIXME: assets should be able to have extensions from their parent item
180-
// let asset = collection.assets.get("example").unwrap();
181-
// let authentication: Authentication = asset.extension().unwrap().unwrap();
182-
// assert_eq!(authentication.refs, vec!["signed_url_auth".to_string()]);
183179
}
184180

185181
#[test]
186182
fn item() {
187183
let collection: Item = crate::read("data/auth/item.json").unwrap();
188-
let authentication: Authentication = collection.extension().unwrap().unwrap();
184+
let authentication: Authentication = collection.extension().unwrap();
189185
let _ = authentication.schemes.get("none").unwrap();
190186
}
191187

core/src/extensions/electro_optical.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ impl Extension for ElectroOptical {
7272
#[cfg(test)]
7373
mod tests {
7474
use super::ElectroOptical;
75-
use crate::{Extensions, Item};
75+
use crate::{Fields, Item};
7676

7777
#[test]
7878
fn item() {
7979
let item: Item = crate::read("data/eo/item.json").unwrap();
80-
let _: ElectroOptical = item.extension().unwrap().unwrap();
80+
let _: ElectroOptical = item.extension().unwrap();
8181
}
8282
}

core/src/extensions/mod.rs

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,24 @@
2222
//! ## Usage
2323
//!
2424
//! [Item](crate::Item), [Collection](crate::Collection),
25-
//! [Catalog](crate::Catalog), and [Asset](crate::Asset) all implement the
26-
//! [Extensions] trait, which provides methods to get, set, and remove extension information:
25+
//! [Catalog](crate::Catalog) all implement the [Extensions] trait, which
26+
//! provides methods to get, set, and remove extension information:
2727
//!
2828
//! ```
29-
//! use stac::{Item, Extensions, extensions::{Projection, projection::Centroid}};
29+
//! use stac::{Item, Extensions, Fields, extensions::{Projection, projection::Centroid}};
3030
//! let mut item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
3131
//! assert!(item.has_extension::<Projection>());
3232
//!
3333
//! // Get extension information
34-
//! let mut projection: Projection = item.extension().unwrap().unwrap();
34+
//! let mut projection: Projection = item.extension().unwrap();
3535
//! println!("code: {}", projection.code.as_ref().unwrap());
3636
//!
3737
//! // Set extension information
3838
//! projection.centroid = Some(Centroid { lat: 34.595302, lon: -101.344483 });
39-
//! item.set_extension(projection).unwrap();
39+
//! Extensions::set_extension(&mut item, projection).unwrap();
4040
//!
4141
//! // Remove an extension
42-
//! item.remove_extension::<Projection>();
42+
//! Extensions::remove_extension::<Projection>(&mut item);
4343
//! assert!(!item.has_extension::<Projection>());
4444
//! ```
4545
@@ -122,26 +122,6 @@ pub trait Extensions: Fields {
122122
.any(|extension| extension.starts_with(E::identifier_prefix()))
123123
}
124124

125-
/// Gets an extension's data.
126-
///
127-
/// Returns `Ok(None)` if the object doesn't have the given extension.
128-
///
129-
/// # Examples
130-
///
131-
/// ```
132-
/// use stac::{Item, extensions::{Projection, Extensions}};
133-
/// let item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
134-
/// let projection: Projection = item.extension().unwrap().unwrap();
135-
/// assert_eq!(projection.code.unwrap(), "EPSG:32614");
136-
/// ```
137-
fn extension<E: Extension>(&self) -> Result<Option<E>> {
138-
if self.has_extension::<E>() {
139-
self.fields_with_prefix(E::PREFIX).map(|v| Some(v))
140-
} else {
141-
Ok(None)
142-
}
143-
}
144-
145125
/// Adds an extension's identifier to this object.
146126
///
147127
/// # Examples
@@ -169,10 +149,9 @@ pub trait Extensions: Fields {
169149
/// item.set_extension(projection).unwrap();
170150
/// ```
171151
fn set_extension<E: Extension>(&mut self, extension: E) -> Result<()> {
172-
self.remove_extension::<E>();
173152
self.extensions_mut().push(E::IDENTIFIER.to_string());
174153
self.extensions_mut().dedup();
175-
self.set_fields_with_prefix(E::PREFIX, extension)
154+
Fields::set_extension(self, extension)
176155
}
177156

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

221199
#[test]
222200
fn set_extension_on_asset() {
201+
use crate::Fields;
202+
223203
let mut asset = Asset::new("a/href.tif");
224-
assert!(!asset.has_extension::<Raster>());
225204
let mut band = Band::default();
226205
band.unit = Some("parsecs".to_string());
227206
let raster = Raster { bands: vec![band] };
228207
asset.set_extension(raster).unwrap();
229-
assert!(asset.has_extension::<Raster>());
230208
let mut item = Item::new("an-id");
231209
let _ = item.assets.insert("data".to_string(), asset);
232210
}

core/src/extensions/projection.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,22 @@ impl Projection {
123123
Ok(None)
124124
}
125125
}
126+
127+
/// Returns true if this projection structure is empty.
128+
///
129+
/// # Examples
130+
///
131+
/// ```
132+
/// use stac::extensions::Projection;
133+
///
134+
/// let projection = Projection::default();
135+
/// assert!(projection.is_empty());
136+
/// ```
137+
pub fn is_empty(&self) -> bool {
138+
serde_json::to_value(self)
139+
.map(|v| v == Value::Object(Default::default()))
140+
.unwrap_or(true)
141+
}
126142
}
127143

128144
impl Extension for Projection {
@@ -134,7 +150,7 @@ impl Extension for Projection {
134150
#[cfg(test)]
135151
mod tests {
136152
use super::Projection;
137-
use crate::{Extensions, Item};
153+
use crate::{Fields, Item};
138154

139155
#[cfg(feature = "gdal")]
140156
#[test]
@@ -162,7 +178,7 @@ mod tests {
162178
fn example() {
163179
let item: Item =
164180
crate::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
165-
let projection = item.extension::<Projection>().unwrap().unwrap();
181+
let projection = item.extension::<Projection>().unwrap();
166182
assert_eq!(projection.code.unwrap(), "EPSG:32614");
167183
}
168184
}

core/src/extensions/raster.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ impl Extension for Raster {
119119
const PREFIX: &'static str = "raster";
120120
}
121121

122+
impl Raster {
123+
/// Returns true if this raster structure is empty.
124+
///
125+
/// # Examples
126+
///
127+
/// ```
128+
/// use stac::extensions::Raster;
129+
///
130+
/// let projection = Raster::default();
131+
/// assert!(projection.is_empty());
132+
/// ```
133+
pub fn is_empty(&self) -> bool {
134+
self.bands.is_empty()
135+
}
136+
}
137+
122138
#[cfg(feature = "gdal")]
123139
impl From<gdal::raster::GdalDataType> for DataType {
124140
fn from(value: gdal::raster::GdalDataType) -> Self {

core/src/fields.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Error, Result};
1+
use crate::{Error, Extension, Result};
22
use serde::{de::DeserializeOwned, Serialize};
33
use serde_json::{json, Map, Value};
44

@@ -124,11 +124,59 @@ pub trait Fields {
124124
/// use stac::{Fields, Item, extensions::Projection};
125125
/// let projection = Projection { code: Some("EPSG:4326".to_string()), ..Default::default() };
126126
/// let mut item = Item::new("an-id");
127-
/// item.remove_fields_with_prefix("proj"); // Prefer `Extensions::remove_extension`
127+
/// item.remove_fields_with_prefix("proj"); // Prefer `Fields::remove_extension`
128128
/// ```
129129
fn remove_fields_with_prefix(&mut self, prefix: &str) {
130130
let prefix = format!("{}:", prefix);
131131
self.fields_mut()
132132
.retain(|key, _| !(key.starts_with(&prefix) && key.len() > prefix.len()));
133133
}
134+
135+
/// Gets an extension's data.
136+
///
137+
/// Returns `Ok(None)` if the object doesn't have the given extension.
138+
///
139+
/// # Examples
140+
///
141+
/// ```
142+
/// use stac::{Item, Fields, extensions::Projection};
143+
/// let item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
144+
/// let projection: Projection = item.extension().unwrap();
145+
/// assert_eq!(projection.code.unwrap(), "EPSG:32614");
146+
/// ```
147+
fn extension<E: Extension>(&self) -> Result<E> {
148+
self.fields_with_prefix(E::PREFIX)
149+
}
150+
151+
/// Sets an extension's data into this object.
152+
///
153+
/// This will remove any previous fields from this extension
154+
///
155+
/// # Examples
156+
///
157+
/// ```
158+
/// use stac::{Item, Fields, extensions::Projection};
159+
/// let mut item = Item::new("an-id");
160+
/// let projection = Projection { code: Some("EPSG:4326".to_string()), ..Default::default() };
161+
/// item.set_extension(projection).unwrap();
162+
/// ```
163+
fn set_extension<E: Extension>(&mut self, extension: E) -> Result<()> {
164+
self.remove_extension::<E>();
165+
self.set_fields_with_prefix(E::PREFIX, extension)
166+
}
167+
168+
/// Removes all of the extension's fields from this object.
169+
///
170+
/// # Examples
171+
///
172+
/// ```
173+
/// use stac::{Item, extensions::{Projection, Extensions}};
174+
/// let mut item: Item = stac::read("examples/extensions-collection/proj-example/proj-example.json").unwrap();
175+
/// assert!(item.has_extension::<Projection>());
176+
/// item.remove_extension::<Projection>();
177+
/// assert!(!item.has_extension::<Projection>());
178+
/// ```
179+
fn remove_extension<E: Extension>(&mut self) {
180+
self.remove_fields_with_prefix(E::PREFIX);
181+
}
134182
}

0 commit comments

Comments
 (0)