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 pub mod traits;
21
22 pub mod codec;
23 pub mod http_protocol;
24 pub mod prelude;
25 pub mod protocol;
26 pub mod serde;
27}
28
29pub use schema::shape_id::ShapeId;
30pub use schema::shape_type::ShapeType;
31pub use schema::trait_map::TraitMap;
32pub use schema::trait_type::Trait;
33pub use schema::trait_type::{AnnotationTrait, DocumentTrait, StringTrait};
34
35pub mod prelude {
36 pub use crate::schema::prelude::*;
37}
38
39pub mod serde {
40 pub use crate::schema::serde::*;
41}
42
43pub mod traits {
44 pub use crate::schema::traits::*;
45}
46
47pub mod codec {
48 pub use crate::schema::codec::*;
49}
50
51pub mod protocol {
52 pub use crate::schema::protocol::*;
53}
54
55pub mod http_protocol {
56 pub use crate::schema::http_protocol::*;
57}
58
59use schema::traits as trait_types;
68
69#[derive(Debug)]
70pub struct Schema {
71 id: ShapeId,
72 shape_type: ShapeType,
73 member_name: Option<&'static str>,
75 member_index: Option<usize>,
77 members: SchemaMembers,
79
80 sensitive: Option<trait_types::SensitiveTrait>,
85 json_name: Option<trait_types::JsonNameTrait>,
86 timestamp_format: Option<trait_types::TimestampFormatTrait>,
87 xml_name: Option<trait_types::XmlNameTrait>,
88 xml_attribute: Option<trait_types::XmlAttributeTrait>,
89 xml_flattened: Option<trait_types::XmlFlattenedTrait>,
90 xml_namespace: Option<trait_types::XmlNamespaceTrait>,
91 http_header: Option<trait_types::HttpHeaderTrait>,
92 http_label: Option<trait_types::HttpLabelTrait>,
93 http_payload: Option<trait_types::HttpPayloadTrait>,
94 http_prefix_headers: Option<trait_types::HttpPrefixHeadersTrait>,
95 http_query: Option<trait_types::HttpQueryTrait>,
96 http_query_params: Option<trait_types::HttpQueryParamsTrait>,
97 http_response_code: Option<trait_types::HttpResponseCodeTrait>,
98 http: Option<trait_types::HttpTrait>,
101 streaming: Option<trait_types::StreamingTrait>,
102 event_header: Option<trait_types::EventHeaderTrait>,
103 event_payload: Option<trait_types::EventPayloadTrait>,
104 host_label: Option<trait_types::HostLabelTrait>,
105 media_type: Option<trait_types::MediaTypeTrait>,
106
107 traits: Option<&'static std::sync::LazyLock<TraitMap>>,
109}
110
111#[derive(Debug)]
113enum SchemaMembers {
114 None,
116 Struct { members: &'static [&'static Schema] },
118 List { member: &'static Schema },
120 Map {
122 key: &'static Schema,
123 value: &'static Schema,
124 },
125}
126
127impl Schema {
128 const EMPTY_TRAITS: Self = Self {
130 id: ShapeId::from_static("", "", ""),
131 shape_type: ShapeType::Boolean,
132 member_name: None,
133 member_index: None,
134 members: SchemaMembers::None,
135 sensitive: None,
136 json_name: None,
137 timestamp_format: None,
138 xml_name: None,
139 xml_attribute: None,
140 xml_flattened: None,
141 xml_namespace: None,
142 http_header: None,
143 http_label: None,
144 http_payload: None,
145 http_prefix_headers: None,
146 http_query: None,
147 http_query_params: None,
148 http_response_code: None,
149 http: None,
150 streaming: None,
151 event_header: None,
152 event_payload: None,
153 host_label: None,
154 media_type: None,
155 traits: None,
156 };
157
158 pub const fn new(id: ShapeId, shape_type: ShapeType) -> Self {
160 Self {
161 id,
162 shape_type,
163 ..Self::EMPTY_TRAITS
164 }
165 }
166
167 pub const fn new_struct(
169 id: ShapeId,
170 shape_type: ShapeType,
171 members: &'static [&'static Schema],
172 ) -> Self {
173 Self {
174 id,
175 shape_type,
176 members: SchemaMembers::Struct { members },
177 ..Self::EMPTY_TRAITS
178 }
179 }
180
181 pub const fn new_list(id: ShapeId, member: &'static Schema) -> Self {
183 Self {
184 id,
185 shape_type: ShapeType::List,
186 members: SchemaMembers::List { member },
187 ..Self::EMPTY_TRAITS
188 }
189 }
190
191 pub const fn new_map(id: ShapeId, key: &'static Schema, value: &'static Schema) -> Self {
193 Self {
194 id,
195 shape_type: ShapeType::Map,
196 members: SchemaMembers::Map { key, value },
197 ..Self::EMPTY_TRAITS
198 }
199 }
200
201 pub const fn new_member(
203 id: ShapeId,
204 shape_type: ShapeType,
205 member_name: &'static str,
206 member_index: usize,
207 ) -> Self {
208 Self {
209 id,
210 shape_type,
211 member_name: Some(member_name),
212 member_index: Some(member_index),
213 ..Self::EMPTY_TRAITS
214 }
215 }
216
217 pub fn shape_id(&self) -> &ShapeId {
219 &self.id
220 }
221
222 pub fn shape_type(&self) -> ShapeType {
224 self.shape_type
225 }
226
227 pub fn traits(&self) -> Option<&TraitMap> {
229 self.traits.map(|lazy| &**lazy)
230 }
231
232 pub fn sensitive(&self) -> Option<&trait_types::SensitiveTrait> {
236 self.sensitive.as_ref()
237 }
238
239 pub fn json_name(&self) -> Option<&trait_types::JsonNameTrait> {
241 self.json_name.as_ref()
242 }
243
244 pub fn timestamp_format(&self) -> Option<&trait_types::TimestampFormatTrait> {
246 self.timestamp_format.as_ref()
247 }
248
249 pub fn xml_name(&self) -> Option<&trait_types::XmlNameTrait> {
251 self.xml_name.as_ref()
252 }
253
254 pub fn has_http_response_binding(&self) -> bool {
258 self.http_header.is_some()
259 || self.http_response_code.is_some()
260 || self.http_prefix_headers.is_some()
261 || self.http_payload.is_some()
262 }
263
264 pub fn http_header(&self) -> Option<&trait_types::HttpHeaderTrait> {
265 self.http_header.as_ref()
266 }
267
268 pub fn http_query(&self) -> Option<&trait_types::HttpQueryTrait> {
270 self.http_query.as_ref()
271 }
272
273 pub fn http_label(&self) -> Option<&trait_types::HttpLabelTrait> {
275 self.http_label.as_ref()
276 }
277
278 pub fn http_payload(&self) -> Option<&trait_types::HttpPayloadTrait> {
280 self.http_payload.as_ref()
281 }
282
283 pub fn http_prefix_headers(&self) -> Option<&trait_types::HttpPrefixHeadersTrait> {
285 self.http_prefix_headers.as_ref()
286 }
287
288 pub fn media_type(&self) -> Option<&trait_types::MediaTypeTrait> {
290 self.media_type.as_ref()
291 }
292
293 pub fn http_query_params(&self) -> Option<&trait_types::HttpQueryParamsTrait> {
295 self.http_query_params.as_ref()
296 }
297
298 pub fn http_response_code(&self) -> Option<&trait_types::HttpResponseCodeTrait> {
300 self.http_response_code.as_ref()
301 }
302
303 pub fn http(&self) -> Option<&trait_types::HttpTrait> {
308 self.http.as_ref()
309 }
310
311 pub const fn with_sensitive(mut self) -> Self {
315 self.sensitive = Some(trait_types::SensitiveTrait);
316 self
317 }
318
319 pub const fn with_json_name(mut self, value: &'static str) -> Self {
321 self.json_name = Some(trait_types::JsonNameTrait::new(value));
322 self
323 }
324
325 pub const fn with_timestamp_format(mut self, format: trait_types::TimestampFormat) -> Self {
327 self.timestamp_format = Some(trait_types::TimestampFormatTrait::new(format));
328 self
329 }
330
331 pub const fn with_xml_name(mut self, value: &'static str) -> Self {
333 self.xml_name = Some(trait_types::XmlNameTrait::new(value));
334 self
335 }
336
337 pub const fn with_xml_attribute(mut self) -> Self {
339 self.xml_attribute = Some(trait_types::XmlAttributeTrait);
340 self
341 }
342
343 pub const fn with_xml_flattened(mut self) -> Self {
345 self.xml_flattened = Some(trait_types::XmlFlattenedTrait);
346 self
347 }
348
349 pub const fn with_http_header(mut self, value: &'static str) -> Self {
351 self.http_header = Some(trait_types::HttpHeaderTrait::new(value));
352 self
353 }
354
355 pub const fn with_http_label(mut self) -> Self {
357 self.http_label = Some(trait_types::HttpLabelTrait);
358 self
359 }
360
361 pub const fn with_http_payload(mut self) -> Self {
363 self.http_payload = Some(trait_types::HttpPayloadTrait);
364 self
365 }
366
367 pub const fn with_http_prefix_headers(mut self, value: &'static str) -> Self {
369 self.http_prefix_headers = Some(trait_types::HttpPrefixHeadersTrait::new(value));
370 self
371 }
372
373 pub const fn with_http_query(mut self, value: &'static str) -> Self {
375 self.http_query = Some(trait_types::HttpQueryTrait::new(value));
376 self
377 }
378
379 pub const fn with_http_query_params(mut self) -> Self {
381 self.http_query_params = Some(trait_types::HttpQueryParamsTrait);
382 self
383 }
384
385 pub const fn with_http_response_code(mut self) -> Self {
387 self.http_response_code = Some(trait_types::HttpResponseCodeTrait);
388 self
389 }
390
391 pub const fn with_http(mut self, http: trait_types::HttpTrait) -> Self {
393 self.http = Some(http);
394 self
395 }
396
397 pub const fn with_streaming(mut self) -> Self {
399 self.streaming = Some(trait_types::StreamingTrait);
400 self
401 }
402
403 pub const fn with_event_header(mut self) -> Self {
405 self.event_header = Some(trait_types::EventHeaderTrait);
406 self
407 }
408
409 pub const fn with_event_payload(mut self) -> Self {
411 self.event_payload = Some(trait_types::EventPayloadTrait);
412 self
413 }
414
415 pub const fn with_host_label(mut self) -> Self {
417 self.host_label = Some(trait_types::HostLabelTrait);
418 self
419 }
420
421 pub const fn with_media_type(mut self, value: &'static str) -> Self {
423 self.media_type = Some(trait_types::MediaTypeTrait::new(value));
424 self
425 }
426
427 pub const fn with_xml_namespace(mut self) -> Self {
429 self.xml_namespace = Some(trait_types::XmlNamespaceTrait);
430 self
431 }
432
433 pub const fn with_traits(mut self, traits: &'static std::sync::LazyLock<TraitMap>) -> Self {
435 self.traits = Some(traits);
436 self
437 }
438
439 pub fn member_name(&self) -> Option<&str> {
441 self.member_name
442 }
443
444 pub fn member_index(&self) -> Option<usize> {
449 self.member_index
450 }
451
452 pub fn member_schema(&self, name: &str) -> Option<&Schema> {
454 match &self.members {
455 SchemaMembers::Struct { members } => members
456 .iter()
457 .find(|m| m.member_name == Some(name))
458 .copied(),
459 _ => None,
460 }
461 }
462
463 pub fn member_schema_by_index(&self, index: usize) -> Option<&Schema> {
468 match &self.members {
469 SchemaMembers::Struct { members } => members.get(index).copied(),
470 _ => None,
471 }
472 }
473
474 pub fn members(&self) -> &[&Schema] {
476 match &self.members {
477 SchemaMembers::Struct { members } => members,
478 _ => &[],
479 }
480 }
481
482 pub fn member(&self) -> Option<&Schema> {
484 match &self.members {
485 SchemaMembers::List { member } => Some(member),
486 SchemaMembers::Map { value, .. } => Some(value),
487 _ => None,
488 }
489 }
490
491 pub fn key(&self) -> Option<&Schema> {
493 match &self.members {
494 SchemaMembers::Map { key, .. } => Some(key),
495 _ => None,
496 }
497 }
498
499 pub fn is_member(&self) -> bool {
503 self.shape_type.is_member()
504 }
505
506 pub fn is_structure(&self) -> bool {
508 self.shape_type == ShapeType::Structure
509 }
510
511 pub fn is_union(&self) -> bool {
513 self.shape_type == ShapeType::Union
514 }
515
516 pub fn is_list(&self) -> bool {
518 self.shape_type == ShapeType::List
519 }
520
521 pub fn is_map(&self) -> bool {
523 self.shape_type == ShapeType::Map
524 }
525
526 pub fn is_blob(&self) -> bool {
528 self.shape_type == ShapeType::Blob
529 }
530
531 pub fn is_string(&self) -> bool {
533 self.shape_type == ShapeType::String
534 }
535}
536
537#[cfg(test)]
538mod test {
539 use crate::{shape_id, Schema, ShapeType, Trait, TraitMap};
540
541 #[derive(Debug)]
543 struct TestTrait {
544 id: crate::ShapeId,
545 #[allow(dead_code)]
546 value: String,
547 }
548
549 impl Trait for TestTrait {
550 fn trait_id(&self) -> &crate::ShapeId {
551 &self.id
552 }
553
554 fn as_any(&self) -> &dyn std::any::Any {
555 self
556 }
557 }
558
559 #[test]
560 fn test_shape_type_simple() {
561 assert!(ShapeType::String.is_simple());
562 assert!(ShapeType::Integer.is_simple());
563 assert!(ShapeType::Boolean.is_simple());
564 assert!(!ShapeType::Structure.is_simple());
565 assert!(!ShapeType::List.is_simple());
566 }
567
568 #[test]
569 fn test_shape_type_aggregate() {
570 assert!(ShapeType::Structure.is_aggregate());
571 assert!(ShapeType::Union.is_aggregate());
572 assert!(ShapeType::List.is_aggregate());
573 assert!(ShapeType::Map.is_aggregate());
574 assert!(!ShapeType::String.is_aggregate());
575 }
576
577 #[test]
578 fn test_shape_type_member() {
579 assert!(ShapeType::Member.is_member());
580 assert!(!ShapeType::String.is_member());
581 assert!(!ShapeType::Structure.is_member());
582 }
583
584 #[test]
585 fn test_shape_id_parsing() {
586 let id = shape_id!("smithy.api", "String");
587 assert_eq!(id.namespace(), "smithy.api");
588 assert_eq!(id.shape_name(), "String");
589 assert_eq!(id.member_name(), None);
590 }
591
592 #[test]
593 fn test_shape_id_with_member() {
594 let id = shape_id!("com.example", "MyStruct", "member");
595 assert_eq!(id.namespace(), "com.example");
596 assert_eq!(id.shape_name(), "MyStruct");
597 assert_eq!(id.member_name(), Some("member"));
598 }
599
600 #[test]
601 fn test_trait_map() {
602 let mut map = TraitMap::new();
603 assert!(map.is_empty());
604 assert_eq!(map.len(), 0);
605
606 let trait_id = shape_id!("smithy.api", "required");
607 let test_trait = Box::new(TestTrait {
608 id: trait_id,
609 value: "test".to_string(),
610 });
611
612 map.insert(test_trait);
613 assert!(!map.is_empty());
614 assert_eq!(map.len(), 1);
615 assert!(map.contains(&trait_id));
616
617 let retrieved = map.get(&trait_id);
618 assert!(retrieved.is_some());
619 }
620
621 #[test]
622 fn test_schema_predicates() {
623 let schema = Schema::new(shape_id!("com.example", "MyStruct"), ShapeType::Structure);
624
625 assert!(schema.is_structure());
626 assert!(!schema.is_union());
627 assert!(!schema.is_list());
628 assert!(!schema.is_member());
629 }
630
631 #[test]
632 fn test_schema_basic() {
633 let schema = Schema::new(shape_id!("smithy.api", "String"), ShapeType::String);
634
635 assert_eq!(schema.shape_id().as_str(), "smithy.api#String");
636 assert_eq!(schema.shape_type(), ShapeType::String);
637 assert!(schema.traits().is_none());
638 assert!(schema.member_name().is_none());
639 assert!(schema.member_schema("test").is_none());
640 assert!(schema.member_schema_by_index(0).is_none());
641 }
642}