1#![cfg_attr(docsrs, feature(doc_cfg))]
8mod schema {
16 pub mod shape_id;
17 pub mod shape_type;
18 pub mod trait_map;
19 pub mod trait_type;
20
21 pub mod codec;
22 pub mod prelude;
23 pub mod serde;
24}
25
26pub use schema::shape_id::ShapeId;
27pub use schema::shape_type::ShapeType;
28pub use schema::trait_map::TraitMap;
29pub use schema::trait_type::Trait;
30
31pub mod prelude {
32 pub use crate::schema::prelude::*;
33}
34
35pub mod serde {
36 pub use crate::schema::serde::*;
37}
38
39pub mod codec {
40 pub use crate::schema::codec::*;
41}
42
43pub trait Schema: Send + Sync {
48 fn shape_id(&self) -> &ShapeId;
50
51 fn shape_type(&self) -> ShapeType;
53
54 fn traits(&self) -> &TraitMap;
56
57 fn member_name(&self) -> Option<&str> {
59 None
60 }
61
62 fn member_schema(&self, _name: &str) -> Option<&dyn Schema> {
64 None
65 }
66
67 fn member_schema_by_index(&self, _index: usize) -> Option<(&str, &dyn Schema)> {
72 None
73 }
74
75 fn member(&self) -> Option<&dyn Schema> {
77 None
78 }
79
80 fn key(&self) -> Option<&dyn Schema> {
82 None
83 }
84
85 fn members(&self) -> Box<dyn Iterator<Item = (&str, &dyn Schema)> + '_> {
87 Box::new(std::iter::empty())
88 }
89
90 fn member_index(&self) -> Option<usize> {
95 None
96 }
97}
98
99pub trait SchemaExt: Schema {
101 fn is_member(&self) -> bool {
103 self.shape_type().is_member()
104 }
105
106 fn is_structure(&self) -> bool {
108 self.shape_type() == ShapeType::Structure
109 }
110
111 fn is_union(&self) -> bool {
113 self.shape_type() == ShapeType::Union
114 }
115
116 fn is_list(&self) -> bool {
118 self.shape_type() == ShapeType::List
119 }
120
121 fn is_map(&self) -> bool {
123 self.shape_type() == ShapeType::Map
124 }
125
126 fn is_blob(&self) -> bool {
128 self.shape_type() == ShapeType::Blob
129 }
130
131 fn is_string(&self) -> bool {
133 self.shape_type() == ShapeType::String
134 }
135}
136
137impl<T: Schema + ?Sized> SchemaExt for T {}
138
139#[cfg(test)]
140mod test {
141 use crate::{Schema, SchemaExt, ShapeId, ShapeType, Trait, TraitMap};
142
143 #[derive(Debug)]
145 struct TestTrait {
146 id: ShapeId,
147 #[allow(dead_code)]
148 value: String,
149 }
150
151 impl Trait for TestTrait {
152 fn trait_id(&self) -> &ShapeId {
153 &self.id
154 }
155
156 fn as_any(&self) -> &dyn std::any::Any {
157 self
158 }
159 }
160
161 struct TestSchema {
163 id: ShapeId,
164 shape_type: ShapeType,
165 traits: TraitMap,
166 }
167
168 impl Schema for TestSchema {
169 fn shape_id(&self) -> &ShapeId {
170 &self.id
171 }
172
173 fn shape_type(&self) -> ShapeType {
174 self.shape_type
175 }
176
177 fn traits(&self) -> &TraitMap {
178 &self.traits
179 }
180 }
181
182 #[test]
183 fn test_shape_type_simple() {
184 assert!(ShapeType::String.is_simple());
185 assert!(ShapeType::Integer.is_simple());
186 assert!(ShapeType::Boolean.is_simple());
187 assert!(!ShapeType::Structure.is_simple());
188 assert!(!ShapeType::List.is_simple());
189 }
190
191 #[test]
192 fn test_shape_type_aggregate() {
193 assert!(ShapeType::Structure.is_aggregate());
194 assert!(ShapeType::Union.is_aggregate());
195 assert!(ShapeType::List.is_aggregate());
196 assert!(ShapeType::Map.is_aggregate());
197 assert!(!ShapeType::String.is_aggregate());
198 }
199
200 #[test]
201 fn test_shape_type_member() {
202 assert!(ShapeType::Member.is_member());
203 assert!(!ShapeType::String.is_member());
204 assert!(!ShapeType::Structure.is_member());
205 }
206
207 #[test]
208 fn test_shape_id_parsing() {
209 let id = ShapeId::new("smithy.api#String");
210 assert_eq!(id.namespace(), "smithy.api");
211 assert_eq!(id.shape_name(), "String");
212 assert_eq!(id.member_name(), None);
213 }
214
215 #[test]
216 fn test_shape_id_with_member() {
217 let id = ShapeId::new("com.example#MyStruct$member");
218 assert_eq!(id.namespace(), "com.example");
219 assert_eq!(id.shape_name(), "MyStruct");
220 assert_eq!(id.member_name(), Some("member"));
221 }
222
223 #[test]
224 fn test_trait_map() {
225 let mut map = TraitMap::new();
226 assert!(map.is_empty());
227 assert_eq!(map.len(), 0);
228
229 let trait_id = ShapeId::new("smithy.api#required");
230 let test_trait = Box::new(TestTrait {
231 id: trait_id.clone(),
232 value: "test".to_string(),
233 });
234
235 map.insert(test_trait);
236 assert!(!map.is_empty());
237 assert_eq!(map.len(), 1);
238 assert!(map.contains(&trait_id));
239
240 let retrieved = map.get(&trait_id);
241 assert!(retrieved.is_some());
242 }
243
244 #[test]
245 fn test_schema_ext() {
246 let schema = TestSchema {
247 id: ShapeId::new("com.example#MyStruct"),
248 shape_type: ShapeType::Structure,
249 traits: TraitMap::new(),
250 };
251
252 assert!(schema.is_structure());
253 assert!(!schema.is_union());
254 assert!(!schema.is_list());
255 assert!(!schema.is_member());
256 }
257
258 #[test]
259 fn test_schema_basic() {
260 let schema = TestSchema {
261 id: ShapeId::new("smithy.api#String"),
262 shape_type: ShapeType::String,
263 traits: TraitMap::new(),
264 };
265
266 assert_eq!(schema.shape_id().as_str(), "smithy.api#String");
267 assert_eq!(schema.shape_type(), ShapeType::String);
268 assert!(schema.traits().is_empty());
269 assert!(schema.member_name().is_none());
270 assert!(schema.member_schema("test").is_none());
271 assert!(schema.member_schema_by_index(0).is_none());
272 }
273}