use crate::{Result, api::Item};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::{Map, Value};
use stac::Link;
use stac_derive::{Links, SelfHref};
const ITEM_COLLECTION_TYPE: &str = "FeatureCollection";
fn item_collection_type() -> String {
ITEM_COLLECTION_TYPE.to_string()
}
fn deserialize_item_collection_type<'de, D>(
deserializer: D,
) -> std::result::Result<String, D::Error>
where
D: Deserializer<'de>,
{
let r#type = String::deserialize(deserializer)?;
if r#type != ITEM_COLLECTION_TYPE {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&r#type),
&ITEM_COLLECTION_TYPE,
))
} else {
Ok(r#type)
}
}
#[derive(Debug, Serialize, Deserialize, Default, Links, SelfHref)]
pub struct ItemCollection {
#[serde(
default = "item_collection_type",
deserialize_with = "deserialize_item_collection_type"
)]
r#type: String,
#[serde(rename = "features")]
pub items: Vec<Item>,
pub links: Vec<Link>,
#[serde(skip_serializing_if = "Option::is_none", rename = "numberMatched")]
pub number_matched: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", rename = "numberReturned")]
pub number_returned: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<Context>,
#[serde(flatten)]
pub additional_fields: Map<String, Value>,
#[serde(skip)]
pub next: Option<Map<String, Value>>,
#[serde(skip)]
pub prev: Option<Map<String, Value>>,
#[serde(skip)]
pub first: Option<Map<String, Value>>,
#[serde(skip)]
pub last: Option<Map<String, Value>>,
#[serde(skip)]
self_href: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Context {
pub returned: u64,
pub limit: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matched: Option<u64>,
#[serde(flatten)]
pub additional_fields: Map<String, Value>,
}
impl ItemCollection {
pub fn new(items: Vec<Item>) -> Result<ItemCollection> {
let number_returned = items.len();
Ok(ItemCollection {
r#type: item_collection_type(),
items,
links: Vec::new(),
number_matched: None,
number_returned: Some(number_returned.try_into()?),
context: None,
additional_fields: Map::new(),
next: None,
prev: None,
first: None,
last: None,
self_href: None,
})
}
}
impl From<Vec<Item>> for ItemCollection {
fn from(items: Vec<Item>) -> Self {
ItemCollection {
r#type: item_collection_type(),
items,
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::ItemCollection;
#[test]
fn serialize_type_field() {
let item_collection = ItemCollection::from(vec![]);
let value = serde_json::to_value(item_collection).unwrap();
assert_eq!(value.as_object().unwrap()["type"], "FeatureCollection");
}
}