stac_api/
item_collection.rs1use crate::{Item, Result};
2use serde::{Deserialize, Deserializer, Serialize};
3use serde_json::{Map, Value};
4use stac::Link;
5use stac_derive::{Links, SelfHref};
6
7const ITEM_COLLECTION_TYPE: &str = "FeatureCollection";
8
9fn item_collection_type() -> String {
10 ITEM_COLLECTION_TYPE.to_string()
11}
12
13fn deserialize_item_collection_type<'de, D>(
14 deserializer: D,
15) -> std::result::Result<String, D::Error>
16where
17 D: Deserializer<'de>,
18{
19 let r#type = String::deserialize(deserializer)?;
20 if r#type != ITEM_COLLECTION_TYPE {
21 Err(serde::de::Error::invalid_value(
22 serde::de::Unexpected::Str(&r#type),
23 &ITEM_COLLECTION_TYPE,
24 ))
25 } else {
26 Ok(r#type)
27 }
28}
29
30#[derive(Debug, Serialize, Deserialize, Default, Links, SelfHref)]
37pub struct ItemCollection {
38 #[serde(
39 default = "item_collection_type",
40 deserialize_with = "deserialize_item_collection_type"
41 )]
42 r#type: String,
43
44 #[serde(rename = "features")]
46 pub items: Vec<Item>,
47
48 pub links: Vec<Link>,
50
51 #[serde(skip_serializing_if = "Option::is_none", rename = "numberMatched")]
53 pub number_matched: Option<u64>,
54
55 #[serde(skip_serializing_if = "Option::is_none", rename = "numberReturned")]
57 pub number_returned: Option<u64>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
63 pub context: Option<Context>,
64
65 #[serde(flatten)]
67 pub additional_fields: Map<String, Value>,
68
69 #[serde(skip)]
74 pub next: Option<Map<String, Value>>,
75
76 #[serde(skip)]
81 pub prev: Option<Map<String, Value>>,
82
83 #[serde(skip)]
88 pub first: Option<Map<String, Value>>,
89
90 #[serde(skip)]
95 pub last: Option<Map<String, Value>>,
96
97 #[serde(skip)]
98 self_href: Option<String>,
99}
100
101#[derive(Debug, Serialize, Deserialize)]
105pub struct Context {
106 pub returned: u64,
109
110 pub limit: Option<u64>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
116 pub matched: Option<u64>,
117
118 #[serde(flatten)]
120 pub additional_fields: Map<String, Value>,
121}
122
123impl ItemCollection {
124 pub fn new(items: Vec<Item>) -> Result<ItemCollection> {
133 let number_returned = items.len();
134 Ok(ItemCollection {
135 r#type: item_collection_type(),
136 items,
137 links: Vec::new(),
138 number_matched: None,
139 number_returned: Some(number_returned.try_into()?),
140 context: None,
141 additional_fields: Map::new(),
142 next: None,
143 prev: None,
144 first: None,
145 last: None,
146 self_href: None,
147 })
148 }
149}
150
151impl From<Vec<Item>> for ItemCollection {
152 fn from(items: Vec<Item>) -> Self {
153 ItemCollection {
154 r#type: item_collection_type(),
155 items,
156 ..Default::default()
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::ItemCollection;
164
165 #[test]
166 fn serialize_type_field() {
167 let item_collection = ItemCollection::from(vec![]);
168 let value = serde_json::to_value(item_collection).unwrap();
169 assert_eq!(value.as_object().unwrap()["type"], "FeatureCollection");
170 }
171}