oxigdal_stac/api/
conformance.rs1use serde::{Deserialize, Serialize};
6
7pub mod uris {
9 pub const CORE: &str = "https://api.stacspec.org/v1.0.0/core";
11 pub const BROWSEABLE: &str = "https://api.stacspec.org/v1.0.0/browseable";
13 pub const ITEM_SEARCH: &str = "https://api.stacspec.org/v1.0.0/item-search";
15 pub const ITEM_SEARCH_FILTER: &str = "https://api.stacspec.org/v1.0.0/item-search#filter";
17 pub const ITEM_SEARCH_SORT: &str = "https://api.stacspec.org/v1.0.0/item-search#sort";
19 pub const ITEM_SEARCH_FIELDS: &str = "https://api.stacspec.org/v1.0.0/item-search#fields";
21 pub const OGCAPI_FEATURES: &str = "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core";
23 pub const TRANSACTION: &str =
25 "https://api.stacspec.org/v1.0.0/ogcapi-features/extensions/transaction";
26 pub const CHILDREN: &str = "https://api.stacspec.org/v1.0.0/children";
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35pub struct ConformanceDeclaration {
36 #[serde(rename = "conformsTo")]
38 pub conforms_to: Vec<String>,
39}
40
41impl ConformanceDeclaration {
42 pub fn new(classes: impl IntoIterator<Item = impl Into<String>>) -> Self {
44 Self {
45 conforms_to: classes.into_iter().map(Into::into).collect(),
46 }
47 }
48
49 pub fn supports(&self, class: &str) -> bool {
51 self.conforms_to.iter().any(|c| c == class)
52 }
53
54 pub fn standard() -> Self {
59 Self::new([
60 uris::CORE,
61 uris::BROWSEABLE,
62 uris::ITEM_SEARCH,
63 uris::ITEM_SEARCH_FILTER,
64 uris::ITEM_SEARCH_SORT,
65 uris::ITEM_SEARCH_FIELDS,
66 uris::OGCAPI_FEATURES,
67 uris::CHILDREN,
68 ])
69 }
70
71 pub fn with_transaction(mut self) -> Self {
73 self.conforms_to.push(uris::TRANSACTION.to_string());
74 self
75 }
76
77 pub fn with_class(mut self, uri: impl Into<String>) -> Self {
79 self.conforms_to.push(uri.into());
80 self
81 }
82
83 pub fn len(&self) -> usize {
85 self.conforms_to.len()
86 }
87
88 pub fn is_empty(&self) -> bool {
90 self.conforms_to.is_empty()
91 }
92}
93
94impl Default for ConformanceDeclaration {
95 fn default() -> Self {
96 Self::standard()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_standard_includes_core() {
106 let decl = ConformanceDeclaration::standard();
107 assert!(decl.supports(uris::CORE));
108 }
109
110 #[test]
111 fn test_standard_includes_item_search() {
112 let decl = ConformanceDeclaration::standard();
113 assert!(decl.supports(uris::ITEM_SEARCH));
114 }
115
116 #[test]
117 fn test_standard_includes_ogc() {
118 let decl = ConformanceDeclaration::standard();
119 assert!(decl.supports(uris::OGCAPI_FEATURES));
120 }
121
122 #[test]
123 fn test_supports_false_for_unknown() {
124 let decl = ConformanceDeclaration::standard();
125 assert!(!decl.supports("https://example.com/unknown"));
126 }
127
128 #[test]
129 fn test_with_transaction() {
130 let decl = ConformanceDeclaration::standard().with_transaction();
131 assert!(decl.supports(uris::TRANSACTION));
132 }
133
134 #[test]
135 fn test_custom_class() {
136 let decl = ConformanceDeclaration::new(["https://my.server/custom"]);
137 assert!(decl.supports("https://my.server/custom"));
138 }
139
140 #[test]
141 fn test_json_roundtrip() {
142 let decl = ConformanceDeclaration::standard();
143 let json = serde_json::to_string(&decl).expect("serialize");
144 let back: ConformanceDeclaration = serde_json::from_str(&json).expect("deserialize");
145 assert_eq!(decl, back);
146 }
147
148 #[test]
149 fn test_standard_count() {
150 let decl = ConformanceDeclaration::standard();
151 assert_eq!(decl.len(), 8);
153 }
154}