From 844ff5ea89fa6462f27460efd8c247a7f27dd016 Mon Sep 17 00:00:00 2001 From: Landon James Date: Tue, 10 Feb 2026 15:01:58 -0800 Subject: [PATCH 1/5] First pass at types for Smithy schemas --- rust-runtime/Cargo.lock | 2 +- rust-runtime/aws-smithy-types/Cargo.toml | 2 +- rust-runtime/aws-smithy-types/src/lib.rs | 2 + rust-runtime/aws-smithy-types/src/schema.rs | 251 ++++++++++++++++++ .../aws-smithy-types/src/schema/shape_id.rs | 155 +++++++++++ .../aws-smithy-types/src/schema/shape_type.rs | 89 +++++++ .../aws-smithy-types/src/schema/trait_map.rs | 55 ++++ .../aws-smithy-types/src/schema/trait_type.rs | 20 ++ 8 files changed, 574 insertions(+), 2 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/schema.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/shape_id.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/shape_type.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/trait_map.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/trait_type.rs diff --git a/rust-runtime/Cargo.lock b/rust-runtime/Cargo.lock index 8c31b17f896..5c049a0f095 100644 --- a/rust-runtime/Cargo.lock +++ b/rust-runtime/Cargo.lock @@ -701,7 +701,7 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.4.3" +version = "1.4.5" dependencies = [ "base64 0.13.1", "base64-simd", diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index c931002f473..031ddd0d372 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-types" -version = "1.4.3" +version = "1.4.5" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 6f893b70a1e..9f99d3c88ab 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -30,6 +30,8 @@ pub mod error; pub mod event_stream; pub mod primitive; pub mod retry; +/// Runtime schema types for Smithy shapes. +pub mod schema; pub mod timeout; /// Utilities for type erasure. diff --git a/rust-runtime/aws-smithy-types/src/schema.rs b/rust-runtime/aws-smithy-types/src/schema.rs new file mode 100644 index 00000000000..0ab2f02abc0 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema.rs @@ -0,0 +1,251 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime schema types for Smithy shapes. +//! +//! This module provides the core types for representing Smithy schemas at runtime, +//! enabling protocol-agnostic serialization and deserialization. + +mod shape_id; +mod shape_type; +mod trait_map; +mod trait_type; + +pub use shape_id::ShapeId; +pub use shape_type::ShapeType; +pub use trait_map::TraitMap; +pub use trait_type::Trait; + +/// Core trait representing a Smithy schema at runtime. +/// +/// A schema is a lightweight runtime representation of a Smithy shape, +/// containing the shape's ID, type, traits, and references to member schemas. +pub trait Schema: Send + Sync { + /// Returns the Shape ID of this schema. + fn shape_id(&self) -> &ShapeId; + + /// Returns the shape type. + fn shape_type(&self) -> ShapeType; + + /// Returns the traits associated with this schema. + fn traits(&self) -> &TraitMap; + + /// Returns the member name if this is a member schema. + fn member_name(&self) -> Option<&str> { + None + } + + /// Returns the member schema by name (for structures and unions). + fn member_schema(&self, _name: &str) -> Option<&dyn Schema> { + None + } + + /// Returns the member schema by position index (for structures and unions). + /// + /// This is an optimization for generated code to avoid string lookups. + /// Consumer code should not rely on specific position values as they may change. + fn member_schema_by_index(&self, _index: usize) -> Option<&dyn Schema> { + None + } + + /// Returns the member schema for collections (list member or map value). + fn member(&self) -> Option<&dyn Schema> { + None + } + + /// Returns the key schema for maps. + fn key(&self) -> Option<&dyn Schema> { + None + } + + /// Returns an iterator over member schemas (for structures and unions). + fn members(&self) -> Box + '_> { + Box::new(std::iter::empty()) + } + + /// Returns the member index for member schemas. + /// + /// This is used internally by generated code for efficient member lookup. + /// Returns None if not applicable or not a member schema. + fn member_index(&self) -> Option { + None + } +} + +/// Helper methods for Schema trait. +pub trait SchemaExt: Schema { + /// Returns true if this is a member schema. + fn is_member(&self) -> bool { + self.shape_type().is_member() + } + + /// Returns true if this is a structure schema. + fn is_structure(&self) -> bool { + self.shape_type() == ShapeType::Structure + } + + /// Returns true if this is a union schema. + fn is_union(&self) -> bool { + self.shape_type() == ShapeType::Union + } + + /// Returns true if this is a list schema. + fn is_list(&self) -> bool { + self.shape_type() == ShapeType::List + } + + /// Returns true if this is a map schema. + fn is_map(&self) -> bool { + self.shape_type() == ShapeType::Map + } + + /// Returns true if this is a blob schema. + fn is_blob(&self) -> bool { + self.shape_type() == ShapeType::Blob + } + + /// Returns true if this is a string schema. + fn is_string(&self) -> bool { + self.shape_type() == ShapeType::String + } +} + +impl SchemaExt for T {} + +#[cfg(test)] +mod tests { + use crate::schema::{Schema, SchemaExt, ShapeId, ShapeType, Trait, TraitMap}; + + // Simple test trait implementation + #[derive(Debug)] + struct TestTrait { + id: ShapeId, + #[allow(dead_code)] + value: String, + } + + impl Trait for TestTrait { + fn trait_id(&self) -> &ShapeId { + &self.id + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + } + + // Simple test schema implementation + struct TestSchema { + id: ShapeId, + shape_type: ShapeType, + traits: TraitMap, + } + + impl Schema for TestSchema { + fn shape_id(&self) -> &ShapeId { + &self.id + } + + fn shape_type(&self) -> ShapeType { + self.shape_type + } + + fn traits(&self) -> &TraitMap { + &self.traits + } + } + + #[test] + fn test_shape_type_simple() { + assert!(ShapeType::String.is_simple()); + assert!(ShapeType::Integer.is_simple()); + assert!(ShapeType::Boolean.is_simple()); + assert!(!ShapeType::Structure.is_simple()); + assert!(!ShapeType::List.is_simple()); + } + + #[test] + fn test_shape_type_aggregate() { + assert!(ShapeType::Structure.is_aggregate()); + assert!(ShapeType::Union.is_aggregate()); + assert!(ShapeType::List.is_aggregate()); + assert!(ShapeType::Map.is_aggregate()); + assert!(!ShapeType::String.is_aggregate()); + } + + #[test] + fn test_shape_type_member() { + assert!(ShapeType::Member.is_member()); + assert!(!ShapeType::String.is_member()); + assert!(!ShapeType::Structure.is_member()); + } + + #[test] + fn test_shape_id_parsing() { + let id = ShapeId::new("smithy.api#String"); + assert_eq!(id.namespace(), Some("smithy.api")); + assert_eq!(id.shape_name(), Some("String")); + assert_eq!(id.member_name(), None); + } + + #[test] + fn test_shape_id_with_member() { + let id = ShapeId::new("com.example#MyStruct$member"); + assert_eq!(id.namespace(), Some("com.example")); + assert_eq!(id.shape_name(), Some("MyStruct")); + assert_eq!(id.member_name(), Some("member")); + } + + #[test] + fn test_trait_map() { + let mut map = TraitMap::new(); + assert!(map.is_empty()); + assert_eq!(map.len(), 0); + + let trait_id = ShapeId::new("smithy.api#required"); + let test_trait = Box::new(TestTrait { + id: trait_id.clone(), + value: "test".to_string(), + }); + + map.insert(test_trait); + assert!(!map.is_empty()); + assert_eq!(map.len(), 1); + assert!(map.contains(&trait_id)); + + let retrieved = map.get(&trait_id); + assert!(retrieved.is_some()); + } + + #[test] + fn test_schema_ext() { + let schema = TestSchema { + id: ShapeId::new("com.example#MyStruct"), + shape_type: ShapeType::Structure, + traits: TraitMap::new(), + }; + + assert!(schema.is_structure()); + assert!(!schema.is_union()); + assert!(!schema.is_list()); + assert!(!schema.is_member()); + } + + #[test] + fn test_schema_basic() { + let schema = TestSchema { + id: ShapeId::new("smithy.api#String"), + shape_type: ShapeType::String, + traits: TraitMap::new(), + }; + + assert_eq!(schema.shape_id().as_str(), "smithy.api#String"); + assert_eq!(schema.shape_type(), ShapeType::String); + assert!(schema.traits().is_empty()); + assert!(schema.member_name().is_none()); + assert!(schema.member_schema("test").is_none()); + assert!(schema.member_schema_by_index(0).is_none()); + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs new file mode 100644 index 00000000000..aa72480072b --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs @@ -0,0 +1,155 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// A Smithy Shape ID. +/// +/// Shape IDs uniquely identify shapes in a Smithy model. +/// Format: `namespace#shapeName` or `namespace#shapeName$memberName` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ShapeId { + value: String, +} + +impl ShapeId { + /// Creates a new ShapeId from a string. + /// + /// # Examples + /// ``` + /// use aws_smithy_types::schema::ShapeId; + /// + /// let shape_id = ShapeId::new("smithy.api#String"); + /// ``` + pub fn new(value: impl Into) -> Self { + Self { + value: value.into(), + } + } + + /// Returns the string representation of this ShapeId. + pub fn as_str(&self) -> &str { + &self.value + } + + /// Returns the namespace portion of the ShapeId. + /// + /// # Examples + /// ``` + /// use aws_smithy_types::schema::ShapeId; + /// + /// let shape_id = ShapeId::new("smithy.api#String"); + /// assert_eq!(shape_id.namespace(), Some("smithy.api")); + /// ``` + pub fn namespace(&self) -> Option<&str> { + self.value.split_once('#').map(|(ns, _)| ns) + } + + /// Returns the shape name portion of the ShapeId. + /// + /// # Examples + /// ``` + /// use aws_smithy_types::schema::ShapeId; + /// + /// let shape_id = ShapeId::new("smithy.api#String"); + /// assert_eq!(shape_id.shape_name(), Some("String")); + /// ``` + pub fn shape_name(&self) -> Option<&str> { + self.value + .split_once('#') + .and_then(|(_, rest)| rest.split_once('$').map(|(name, _)| name).or(Some(rest))) + } + + /// Returns the member name if this is a member shape ID. + /// + /// # Examples + /// ``` + /// use aws_smithy_types::schema::ShapeId; + /// + /// let shape_id = ShapeId::new("com.example#MyStruct$member"); + /// assert_eq!(shape_id.member_name(), Some("member")); + /// ``` + pub fn member_name(&self) -> Option<&str> { + self.value + .split_once('#') + .and_then(|(_, rest)| rest.split_once('$').map(|(_, member)| member)) + } +} + +impl From for ShapeId { + fn from(value: String) -> Self { + Self::new(value) + } +} + +impl From<&str> for ShapeId { + fn from(value: &str) -> Self { + Self::new(value) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let shape_id = ShapeId::new("smithy.api#String"); + assert_eq!(shape_id.as_str(), "smithy.api#String"); + } + + #[test] + fn test_namespace() { + assert_eq!( + ShapeId::new("smithy.api#String").namespace(), + Some("smithy.api") + ); + assert_eq!( + ShapeId::new("com.example#MyStruct$member").namespace(), + Some("com.example") + ); + assert_eq!(ShapeId::new("NoNamespace").namespace(), None); + } + + #[test] + fn test_shape_name() { + assert_eq!( + ShapeId::new("smithy.api#String").shape_name(), + Some("String") + ); + assert_eq!( + ShapeId::new("com.example#MyStruct$member").shape_name(), + Some("MyStruct") + ); + assert_eq!(ShapeId::new("NoNamespace").shape_name(), None); + } + + #[test] + fn test_member_name() { + assert_eq!( + ShapeId::new("com.example#MyStruct$member").member_name(), + Some("member") + ); + assert_eq!(ShapeId::new("smithy.api#String").member_name(), None); + assert_eq!(ShapeId::new("NoNamespace").member_name(), None); + } + + #[test] + fn test_from_string() { + let shape_id: ShapeId = String::from("smithy.api#String").into(); + assert_eq!(shape_id.as_str(), "smithy.api#String"); + } + + #[test] + fn test_from_str() { + let shape_id: ShapeId = "smithy.api#String".into(); + assert_eq!(shape_id.as_str(), "smithy.api#String"); + } + + #[test] + fn test_clone_and_equality() { + let shape_id1 = ShapeId::new("smithy.api#String"); + let shape_id2 = shape_id1.clone(); + assert_eq!(shape_id1, shape_id2); + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_type.rs b/rust-runtime/aws-smithy-types/src/schema/shape_type.rs new file mode 100644 index 00000000000..ef80e5939e5 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/shape_type.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Enumeration of Smithy shape types. +/// +/// This represents the core shape types from the Smithy specification, +/// including simple types, aggregate types, and the special member type. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum ShapeType { + // Simple types + /// Boolean type + Boolean, + /// 8-bit signed integer + Byte, + /// 16-bit signed integer + Short, + /// 32-bit signed integer + Integer, + /// 64-bit signed integer + Long, + /// 32-bit floating point + Float, + /// 64-bit floating point + Double, + /// Arbitrary precision integer + BigInteger, + /// Arbitrary precision decimal + BigDecimal, + /// UTF-8 string + String, + /// Binary data + Blob, + /// Timestamp + Timestamp, + /// Document type + Document, + + // Aggregate types + /// List type + List, + /// Map type + Map, + /// Structure type + Structure, + /// Union type + Union, + + // Member + /// Member shape + Member, +} + +impl ShapeType { + /// Returns true if this is a simple type. + #[inline] + pub fn is_simple(&self) -> bool { + matches!( + self, + Self::Boolean + | Self::Byte + | Self::Short + | Self::Integer + | Self::Long + | Self::Float + | Self::Double + | Self::BigInteger + | Self::BigDecimal + | Self::String + | Self::Blob + | Self::Timestamp + | Self::Document + ) + } + + /// Returns true if this is an aggregate type. + #[inline] + pub fn is_aggregate(&self) -> bool { + matches!(self, Self::List | Self::Map | Self::Structure | Self::Union) + } + + /// Returns true if this is a member type. + #[inline] + pub fn is_member(&self) -> bool { + matches!(self, Self::Member) + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs new file mode 100644 index 00000000000..52adb549837 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::{ShapeId, Trait}; +use std::collections::HashMap; + +/// A map of traits keyed by their Shape ID. +/// +/// This provides efficient lookup of traits during serialization and deserialization. +#[derive(Debug, Default)] +pub struct TraitMap { + traits: HashMap>, +} + +impl TraitMap { + /// Creates a new empty TraitMap. + pub fn new() -> Self { + Self { + traits: HashMap::new(), + } + } + + /// Inserts a trait into the map. + pub fn insert(&mut self, trait_obj: Box) { + let id = trait_obj.trait_id().clone(); + self.traits.insert(id, trait_obj); + } + + /// Gets a trait by its Shape ID. + pub fn get(&self, id: &ShapeId) -> Option<&dyn Trait> { + self.traits.get(id).map(|t| t.as_ref()) + } + + /// Returns true if the map contains a trait with the given Shape ID. + pub fn contains(&self, id: &ShapeId) -> bool { + self.traits.contains_key(id) + } + + /// Returns an iterator over all traits. + pub fn iter(&self) -> impl Iterator { + self.traits.values().map(|t| t.as_ref()) + } + + /// Returns the number of traits in the map. + pub fn len(&self) -> usize { + self.traits.len() + } + + /// Returns true if the map is empty. + pub fn is_empty(&self) -> bool { + self.traits.is_empty() + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_type.rs b/rust-runtime/aws-smithy-types/src/schema/trait_type.rs new file mode 100644 index 00000000000..56bc1993dbb --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/trait_type.rs @@ -0,0 +1,20 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::ShapeId; +use std::any::Any; +use std::fmt; + +/// Trait representing a Smithy trait at runtime. +/// +/// Traits provide additional metadata about shapes that affect serialization, +/// validation, and other behaviors. +pub trait Trait: Any + Send + Sync + fmt::Debug { + /// Returns the Shape ID of this trait. + fn trait_id(&self) -> &ShapeId; + + /// Returns this trait as `&dyn Any` for downcasting. + fn as_any(&self) -> &dyn Any; +} From f16ee972e32457ef2a5aef5a1a8a591a5860b053 Mon Sep 17 00:00:00 2001 From: Landon James Date: Thu, 12 Feb 2026 11:54:40 -0800 Subject: [PATCH 2/5] Add schemas for smithy prelude shapes --- .gitignore | 3 + rust-runtime/aws-smithy-types/src/schema.rs | 2 + .../aws-smithy-types/src/schema/prelude.rs | 176 ++++++++++++++++++ .../aws-smithy-types/src/schema/shape_id.rs | 18 +- .../aws-smithy-types/src/schema/trait_map.rs | 38 +++- 5 files changed, 223 insertions(+), 14 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/schema/prelude.rs diff --git a/.gitignore b/.gitignore index aad300982ae..367f0d72aec 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ target/ # python __pycache__ + +# AI context dirs +.kiro/ diff --git a/rust-runtime/aws-smithy-types/src/schema.rs b/rust-runtime/aws-smithy-types/src/schema.rs index 0ab2f02abc0..baf06ac70b1 100644 --- a/rust-runtime/aws-smithy-types/src/schema.rs +++ b/rust-runtime/aws-smithy-types/src/schema.rs @@ -13,6 +13,8 @@ mod shape_type; mod trait_map; mod trait_type; +pub mod prelude; + pub use shape_id::ShapeId; pub use shape_type::ShapeType; pub use trait_map::TraitMap; diff --git a/rust-runtime/aws-smithy-types/src/schema/prelude.rs b/rust-runtime/aws-smithy-types/src/schema/prelude.rs new file mode 100644 index 00000000000..5bf05a10a61 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/prelude.rs @@ -0,0 +1,176 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Prelude schemas for built-in Smithy types. +//! +//! This module provides const schemas for Smithy's prelude types, +//! which are the fundamental types available in all Smithy models. + +use crate::schema::{Schema, ShapeId, ShapeType, TraitMap}; + +/// A simple schema implementation for prelude types. +#[derive(Debug)] +pub struct PreludeSchema { + id: ShapeId, + shape_type: ShapeType, +} + +impl PreludeSchema { + /// Creates a new prelude schema. + pub const fn new(id: ShapeId, shape_type: ShapeType) -> Self { + Self { id, shape_type } + } +} + +impl Schema for PreludeSchema { + fn shape_id(&self) -> &ShapeId { + &self.id + } + + fn shape_type(&self) -> ShapeType { + self.shape_type + } + + fn traits(&self) -> &TraitMap { + static EMPTY_TRAITS: TraitMap = TraitMap::empty(); + &EMPTY_TRAITS + } +} + +// TODO(schema): We should probably test with these as `pub static` too since that could +// theoretically cut down on binary size (at the expense of some runtime performance) + +/// Schema for `smithy.api#String` +pub const STRING: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#String"), ShapeType::String); + +/// Schema for `smithy.api#Boolean` +pub const BOOLEAN: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Boolean"), + ShapeType::Boolean, +); + +/// Schema for `smithy.api#Byte` +pub const BYTE: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Byte"), ShapeType::Byte); + +/// Schema for `smithy.api#Short` +pub const SHORT: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Short"), ShapeType::Short); + +/// Schema for `smithy.api#Integer` +pub const INTEGER: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Integer"), + ShapeType::Integer, +); + +/// Schema for `smithy.api#Long` +pub const LONG: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Long"), ShapeType::Long); + +/// Schema for `smithy.api#Float` +pub const FLOAT: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Float"), ShapeType::Float); + +/// Schema for `smithy.api#Double` +pub const DOUBLE: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Double"), ShapeType::Double); + +/// Schema for `smithy.api#BigInteger` +pub const BIG_INTEGER: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#BigInteger"), + ShapeType::BigInteger, +); + +/// Schema for `smithy.api#BigDecimal` +pub const BIG_DECIMAL: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#BigDecimal"), + ShapeType::BigDecimal, +); + +/// Schema for `smithy.api#Blob` +pub const BLOB: PreludeSchema = + PreludeSchema::new(ShapeId::from_static("smithy.api#Blob"), ShapeType::Blob); + +/// Schema for `smithy.api#Timestamp` +pub const TIMESTAMP: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Timestamp"), + ShapeType::Timestamp, +); + +/// Schema for `smithy.api#Document` +pub const DOCUMENT: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Document"), + ShapeType::Document, +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::schema::SchemaExt; + + #[test] + fn test_string_schema() { + assert_eq!(STRING.shape_id().as_str(), "smithy.api#String"); + assert_eq!(STRING.shape_type(), ShapeType::String); + assert!(STRING.is_string()); + assert!(STRING.traits().is_empty()); + } + + #[test] + fn test_boolean_schema() { + assert_eq!(BOOLEAN.shape_id().as_str(), "smithy.api#Boolean"); + assert_eq!(BOOLEAN.shape_type(), ShapeType::Boolean); + assert!(BOOLEAN.traits().is_empty()); + } + + #[test] + fn test_numeric_schemas() { + assert_eq!(BYTE.shape_type(), ShapeType::Byte); + assert_eq!(SHORT.shape_type(), ShapeType::Short); + assert_eq!(INTEGER.shape_type(), ShapeType::Integer); + assert_eq!(LONG.shape_type(), ShapeType::Long); + assert_eq!(FLOAT.shape_type(), ShapeType::Float); + assert_eq!(DOUBLE.shape_type(), ShapeType::Double); + assert_eq!(BIG_INTEGER.shape_type(), ShapeType::BigInteger); + assert_eq!(BIG_DECIMAL.shape_type(), ShapeType::BigDecimal); + } + + #[test] + fn test_blob_schema() { + assert_eq!(BLOB.shape_id().as_str(), "smithy.api#Blob"); + assert_eq!(BLOB.shape_type(), ShapeType::Blob); + assert!(BLOB.is_blob()); + } + + #[test] + fn test_timestamp_schema() { + assert_eq!(TIMESTAMP.shape_id().as_str(), "smithy.api#Timestamp"); + assert_eq!(TIMESTAMP.shape_type(), ShapeType::Timestamp); + } + + #[test] + fn test_document_schema() { + assert_eq!(DOCUMENT.shape_id().as_str(), "smithy.api#Document"); + assert_eq!(DOCUMENT.shape_type(), ShapeType::Document); + } + + #[test] + fn test_all_prelude_types_are_simple() { + assert!(STRING.shape_type().is_simple()); + assert!(BOOLEAN.shape_type().is_simple()); + assert!(BYTE.shape_type().is_simple()); + assert!(SHORT.shape_type().is_simple()); + assert!(INTEGER.shape_type().is_simple()); + assert!(LONG.shape_type().is_simple()); + assert!(FLOAT.shape_type().is_simple()); + assert!(DOUBLE.shape_type().is_simple()); + assert!(BIG_INTEGER.shape_type().is_simple()); + assert!(BIG_DECIMAL.shape_type().is_simple()); + assert!(BLOB.shape_type().is_simple()); + assert!(TIMESTAMP.shape_type().is_simple()); + assert!(DOCUMENT.shape_type().is_simple()); + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs index aa72480072b..2ca51c308c0 100644 --- a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs +++ b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs @@ -9,10 +9,17 @@ /// Format: `namespace#shapeName` or `namespace#shapeName$memberName` #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ShapeId { - value: String, + value: &'static str, } impl ShapeId { + /// Creates a ShapeId from a static string at compile time. + /// + /// This is used for const initialization of prelude schemas. + pub const fn from_static(value: &'static str) -> Self { + Self { value } + } + /// Creates a new ShapeId from a string. /// /// # Examples @@ -22,14 +29,15 @@ impl ShapeId { /// let shape_id = ShapeId::new("smithy.api#String"); /// ``` pub fn new(value: impl Into) -> Self { + // Leak the string to get a 'static reference Self { - value: value.into(), + value: Box::leak(value.into().into_boxed_str()), } } /// Returns the string representation of this ShapeId. pub fn as_str(&self) -> &str { - &self.value + self.value } /// Returns the namespace portion of the ShapeId. @@ -38,7 +46,7 @@ impl ShapeId { /// ``` /// use aws_smithy_types::schema::ShapeId; /// - /// let shape_id = ShapeId::new("smithy.api#String"); + /// let shape_id = ShapeId::from_static("smithy.api#String"); /// assert_eq!(shape_id.namespace(), Some("smithy.api")); /// ``` pub fn namespace(&self) -> Option<&str> { @@ -51,7 +59,7 @@ impl ShapeId { /// ``` /// use aws_smithy_types::schema::ShapeId; /// - /// let shape_id = ShapeId::new("smithy.api#String"); + /// let shape_id = ShapeId::from_static("smithy.api#String"); /// assert_eq!(shape_id.shape_name(), Some("String")); /// ``` pub fn shape_name(&self) -> Option<&str> { diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs index 52adb549837..609f03a8cdc 100644 --- a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs +++ b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs @@ -9,47 +9,67 @@ use std::collections::HashMap; /// A map of traits keyed by their Shape ID. /// /// This provides efficient lookup of traits during serialization and deserialization. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct TraitMap { - traits: HashMap>, + traits: Option>>, +} + +impl Default for TraitMap { + fn default() -> Self { + Self::new() + } } impl TraitMap { /// Creates a new empty TraitMap. pub fn new() -> Self { Self { - traits: HashMap::new(), + traits: Some(HashMap::new()), } } + /// Creates an empty TraitMap for const contexts. + pub const fn empty() -> Self { + Self { traits: None } + } + /// Inserts a trait into the map. pub fn insert(&mut self, trait_obj: Box) { + if self.traits.is_none() { + self.traits = Some(HashMap::new()); + } let id = trait_obj.trait_id().clone(); - self.traits.insert(id, trait_obj); + self.traits.as_mut().unwrap().insert(id, trait_obj); } /// Gets a trait by its Shape ID. pub fn get(&self, id: &ShapeId) -> Option<&dyn Trait> { - self.traits.get(id).map(|t| t.as_ref()) + self.traits.as_ref()?.get(id).map(|t| t.as_ref()) } /// Returns true if the map contains a trait with the given Shape ID. pub fn contains(&self, id: &ShapeId) -> bool { - self.traits.contains_key(id) + self.traits + .as_ref() + .map(|m| m.contains_key(id)) + .unwrap_or(false) } /// Returns an iterator over all traits. pub fn iter(&self) -> impl Iterator { - self.traits.values().map(|t| t.as_ref()) + self.traits + .as_ref() + .into_iter() + .flat_map(|m| m.values().map(|t| t.as_ref())) } /// Returns the number of traits in the map. pub fn len(&self) -> usize { - self.traits.len() + self.traits.as_ref().map(|m| m.len()).unwrap_or(0) } /// Returns true if the map is empty. pub fn is_empty(&self) -> bool { - self.traits.is_empty() + self.traits.as_ref().map(|m| m.is_empty()).unwrap_or(true) } } From 39b55ba789d23348fd70aff57161c9e5dc302333 Mon Sep 17 00:00:00 2001 From: Landon James Date: Fri, 13 Feb 2026 12:38:03 -0800 Subject: [PATCH 3/5] Add ShapeSerialized and ShapeDeserializer traits --- rust-runtime/Cargo.lock | 2 +- rust-runtime/aws-smithy-types/src/schema.rs | 3 +- .../aws-smithy-types/src/schema/serde.rs | 412 ++++++++++++++++++ .../src/schema/serde/deserializer.rs | 176 ++++++++ .../src/schema/serde/serializer.rs | 159 +++++++ 5 files changed, 750 insertions(+), 2 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/schema/serde.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs create mode 100644 rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs diff --git a/rust-runtime/Cargo.lock b/rust-runtime/Cargo.lock index 1a1fefca5d1..e218a5bfcf5 100644 --- a/rust-runtime/Cargo.lock +++ b/rust-runtime/Cargo.lock @@ -557,7 +557,7 @@ dependencies = [ [[package]] name = "aws-smithy-http-server-python" -version = "0.66.10" +version = "0.67.0" dependencies = [ "aws-smithy-http", "aws-smithy-json", diff --git a/rust-runtime/aws-smithy-types/src/schema.rs b/rust-runtime/aws-smithy-types/src/schema.rs index baf06ac70b1..3c3dc1e8296 100644 --- a/rust-runtime/aws-smithy-types/src/schema.rs +++ b/rust-runtime/aws-smithy-types/src/schema.rs @@ -14,6 +14,7 @@ mod trait_map; mod trait_type; pub mod prelude; +pub mod serde; pub use shape_id::ShapeId; pub use shape_type::ShapeType; @@ -117,7 +118,7 @@ pub trait SchemaExt: Schema { impl SchemaExt for T {} #[cfg(test)] -mod tests { +mod test { use crate::schema::{Schema, SchemaExt, ShapeId, ShapeType, Trait, TraitMap}; // Simple test trait implementation diff --git a/rust-runtime/aws-smithy-types/src/schema/serde.rs b/rust-runtime/aws-smithy-types/src/schema/serde.rs new file mode 100644 index 00000000000..7d93f00083c --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/serde.rs @@ -0,0 +1,412 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Serialization and deserialization interfaces for the Smithy data model. + +mod deserializer; +mod serializer; + +pub use deserializer::ShapeDeserializer; +pub use serializer::{SerializableStruct, ShapeSerializer}; + +#[cfg(test)] +mod test { + use crate::schema::serde::{ShapeDeserializer, ShapeSerializer}; + use crate::schema::{prelude::*, Schema}; + use std::fmt; + + // Mock error type for testing + #[derive(Debug)] + struct MockError(String); + + impl fmt::Display for MockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } + } + + impl std::error::Error for MockError {} + + // Mock serializer for testing + struct MockSerializer { + output: Vec, + } + + impl ShapeSerializer for MockSerializer { + type Output = Vec; + type Error = MockError; + + fn finish(self) -> Result { + Ok(self.output) + } + + fn write_struct( + &mut self, + schema: &dyn Schema, + write_members: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.output + .push(format!("struct({})", schema.shape_id().as_str())); + write_members(self)?; + self.output.push("end_struct".to_string()); + Ok(()) + } + + fn write_list( + &mut self, + schema: &dyn Schema, + write_elements: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.output + .push(format!("list({})", schema.shape_id().as_str())); + write_elements(self)?; + self.output.push("end_list".to_string()); + Ok(()) + } + + fn write_map(&mut self, schema: &dyn Schema, write_entries: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.output + .push(format!("map({})", schema.shape_id().as_str())); + write_entries(self)?; + self.output.push("end_map".to_string()); + Ok(()) + } + + fn write_boolean(&mut self, _schema: &dyn Schema, value: bool) -> Result<(), Self::Error> { + self.output.push(format!("bool({})", value)); + Ok(()) + } + + fn write_byte(&mut self, _schema: &dyn Schema, value: i8) -> Result<(), Self::Error> { + self.output.push(format!("byte({})", value)); + Ok(()) + } + + fn write_short(&mut self, _schema: &dyn Schema, value: i16) -> Result<(), Self::Error> { + self.output.push(format!("short({})", value)); + Ok(()) + } + + fn write_integer(&mut self, _schema: &dyn Schema, value: i32) -> Result<(), Self::Error> { + self.output.push(format!("int({})", value)); + Ok(()) + } + + fn write_long(&mut self, _schema: &dyn Schema, value: i64) -> Result<(), Self::Error> { + self.output.push(format!("long({})", value)); + Ok(()) + } + + fn write_float(&mut self, _schema: &dyn Schema, value: f32) -> Result<(), Self::Error> { + self.output.push(format!("float({})", value)); + Ok(()) + } + + fn write_double(&mut self, _schema: &dyn Schema, value: f64) -> Result<(), Self::Error> { + self.output.push(format!("double({})", value)); + Ok(()) + } + + fn write_big_integer( + &mut self, + _schema: &dyn Schema, + value: &crate::BigInteger, + ) -> Result<(), Self::Error> { + self.output.push(format!("bigint({})", value.as_ref())); + Ok(()) + } + + fn write_big_decimal( + &mut self, + _schema: &dyn Schema, + value: &crate::BigDecimal, + ) -> Result<(), Self::Error> { + self.output.push(format!("bigdec({})", value.as_ref())); + Ok(()) + } + + fn write_string(&mut self, _schema: &dyn Schema, value: &str) -> Result<(), Self::Error> { + self.output.push(format!("string({})", value)); + Ok(()) + } + + fn write_blob( + &mut self, + _schema: &dyn Schema, + value: &crate::Blob, + ) -> Result<(), Self::Error> { + self.output + .push(format!("blob({} bytes)", value.as_ref().len())); + Ok(()) + } + + fn write_timestamp( + &mut self, + _schema: &dyn Schema, + value: &crate::DateTime, + ) -> Result<(), Self::Error> { + self.output.push(format!("timestamp({})", value)); + Ok(()) + } + + fn write_document( + &mut self, + _schema: &dyn Schema, + _value: &crate::Document, + ) -> Result<(), Self::Error> { + self.output.push("document".to_string()); + Ok(()) + } + + fn write_null(&mut self, _schema: &dyn Schema) -> Result<(), Self::Error> { + self.output.push("null".to_string()); + Ok(()) + } + } + + // Mock deserializer for testing + struct MockDeserializer { + values: Vec, + index: usize, + } + + impl MockDeserializer { + fn new(values: Vec) -> Self { + Self { values, index: 0 } + } + } + + impl ShapeDeserializer for MockDeserializer { + type Error = MockError; + + fn read_struct( + &mut self, + _schema: &dyn Schema, + state: T, + mut consumer: F, + ) -> Result + where + F: FnMut(T, &dyn Schema, &mut Self) -> Result, + { + // Simulate reading 2 members + let state = consumer(state, &STRING, self)?; + let state = consumer(state, &INTEGER, self)?; + Ok(state) + } + + fn read_list( + &mut self, + _schema: &dyn Schema, + mut state: T, + mut consumer: F, + ) -> Result + where + F: FnMut(T, &mut Self) -> Result, + { + // Simulate reading 3 elements + for _ in 0..3 { + state = consumer(state, self)?; + } + Ok(state) + } + + fn read_map( + &mut self, + _schema: &dyn Schema, + mut state: T, + mut consumer: F, + ) -> Result + where + F: FnMut(T, String, &mut Self) -> Result, + { + // Simulate reading 2 entries + state = consumer(state, "key1".to_string(), self)?; + state = consumer(state, "key2".to_string(), self)?; + Ok(state) + } + + fn read_boolean(&mut self, _schema: &dyn Schema) -> Result { + Ok(true) + } + + fn read_byte(&mut self, _schema: &dyn Schema) -> Result { + Ok(42) + } + + fn read_short(&mut self, _schema: &dyn Schema) -> Result { + Ok(1000) + } + + fn read_integer(&mut self, _schema: &dyn Schema) -> Result { + Ok(123456) + } + + fn read_long(&mut self, _schema: &dyn Schema) -> Result { + Ok(9876543210) + } + + fn read_float(&mut self, _schema: &dyn Schema) -> Result { + Ok(3.14) + } + + fn read_double(&mut self, _schema: &dyn Schema) -> Result { + Ok(2.71828) + } + + fn read_big_integer( + &mut self, + _schema: &dyn Schema, + ) -> Result { + use std::str::FromStr; + Ok(crate::BigInteger::from_str("12345").unwrap()) + } + + fn read_big_decimal( + &mut self, + _schema: &dyn Schema, + ) -> Result { + use std::str::FromStr; + Ok(crate::BigDecimal::from_str("123.45").unwrap()) + } + + fn read_string(&mut self, _schema: &dyn Schema) -> Result { + if self.index < self.values.len() { + let value = self.values[self.index].clone(); + self.index += 1; + Ok(value) + } else { + Ok("default".to_string()) + } + } + + fn read_blob(&mut self, _schema: &dyn Schema) -> Result { + Ok(crate::Blob::new(vec![1, 2, 3, 4])) + } + + fn read_timestamp(&mut self, _schema: &dyn Schema) -> Result { + Ok(crate::DateTime::from_secs(1234567890)) + } + + fn read_document(&mut self, _schema: &dyn Schema) -> Result { + Ok(crate::Document::Null) + } + + fn is_null(&mut self) -> bool { + false + } + + fn container_size(&mut self) -> Option { + Some(10) + } + } + + #[test] + fn test_serializer_simple_types() { + let mut ser = MockSerializer { output: Vec::new() }; + + ser.write_boolean(&BOOLEAN, true).unwrap(); + ser.write_integer(&INTEGER, 42).unwrap(); + ser.write_string(&STRING, "hello").unwrap(); + + let output = ser.finish().unwrap(); + assert_eq!(output, vec!["bool(true)", "int(42)", "string(hello)"]); + } + + #[test] + fn test_serializer_struct() { + let mut ser = MockSerializer { output: Vec::new() }; + + ser.write_struct(&STRING, |s| { + s.write_string(&STRING, "field1")?; + s.write_integer(&INTEGER, 123)?; + Ok(()) + }) + .unwrap(); + + let output = ser.finish().unwrap(); + assert_eq!( + output, + vec![ + "struct(smithy.api#String)", + "string(field1)", + "int(123)", + "end_struct" + ] + ); + } + + #[test] + fn test_deserializer_simple_types() { + let mut deser = MockDeserializer::new(vec!["test".to_string()]); + + assert_eq!(deser.read_boolean(&BOOLEAN).unwrap(), true); + assert_eq!(deser.read_integer(&INTEGER).unwrap(), 123456); + assert_eq!(deser.read_string(&STRING).unwrap(), "test"); + assert_eq!(deser.container_size(), Some(10)); + assert!(!deser.is_null()); + } + + #[test] + fn test_deserializer_struct() { + let mut deser = MockDeserializer::new(vec!["value1".to_string(), "value2".to_string()]); + + let mut fields = Vec::new(); + deser + .read_struct(&STRING, &mut fields, |fields, _member, d| { + fields.push(d.read_string(&STRING)?); + Ok(fields) + }) + .unwrap(); + + assert_eq!(fields, vec!["value1", "value2"]); + } + + #[test] + fn test_deserializer_list() { + let mut deser = + MockDeserializer::new(vec!["a".to_string(), "b".to_string(), "c".to_string()]); + + let mut elements = Vec::new(); + deser + .read_list(&STRING, &mut elements, |elements, d| { + elements.push(d.read_string(&STRING)?); + Ok(elements) + }) + .unwrap(); + + assert_eq!(elements, vec!["a", "b", "c"]); + } + + #[test] + fn test_deserializer_map() { + let mut deser = MockDeserializer::new(vec!["val1".to_string(), "val2".to_string()]); + + let mut entries = Vec::new(); + deser + .read_map(&STRING, &mut entries, |entries, key, d| { + let value = d.read_string(&STRING)?; + entries.push((key, value)); + Ok(entries) + }) + .unwrap(); + + assert_eq!( + entries, + vec![ + ("key1".to_string(), "val1".to_string()), + ("key2".to_string(), "val2".to_string()) + ] + ); + } +} diff --git a/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs b/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs new file mode 100644 index 00000000000..7387916f9f7 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs @@ -0,0 +1,176 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Shape deserialization interfaces for the Smithy data model. + +use crate::schema::Schema; +use crate::{BigDecimal, BigInteger, Blob, DateTime, Document}; +use std::error::Error; + +/// Deserializes Smithy shapes from a serial format. +/// +/// This trait provides a format-agnostic API for deserializing the Smithy data model. +/// Implementations read from a serial format and create data objects based on schemas. +/// +/// The deserializer uses a consumer pattern for aggregate types (structures, lists, maps) +/// to avoid trait object limitations and enable efficient deserialization without +/// intermediate allocations. +/// +/// # Consumer Pattern +/// +/// For aggregate types, the deserializer calls a consumer function for each element/member. +/// The consumer receives mutable state and updates it with each deserialized value. +/// This pattern: +/// - Avoids trait object issues with generic methods +/// - Enables zero-cost abstractions (closures can be inlined) +/// - Allows caller to control deserialization order and state management +/// - Matches the SEP's recommendation for compiled typed languages +/// +/// # Example +/// +/// ```ignore +/// // Deserializing a structure +/// let mut builder = MyStructBuilder::default(); +/// deserializer.read_struct( +/// &MY_STRUCT_SCHEMA, +/// builder, +/// |mut builder, member, deser| { +/// match member.member_index() { +/// 0 => builder.field1 = Some(deser.read_string(member)?), +/// 1 => builder.field2 = Some(deser.read_i32(member)?), +/// _ => {} +/// } +/// Ok(builder) +/// }, +/// )?; +/// let my_struct = builder.build(); +/// ``` +pub trait ShapeDeserializer { + /// The error type returned by deserialization operations. + type Error: Error; + + /// Reads a structure from the deserializer. + /// + /// The structure deserialization is driven by a consumer callback that is called + /// for each member. The consumer receives the current state, the member schema, + /// and the deserializer, and returns the updated state. + /// + /// # Arguments + /// + /// * `schema` - The schema of the structure being deserialized + /// * `state` - Initial state (typically a builder) + /// * `consumer` - Callback invoked for each member with (state, member_schema, deserializer) + /// + /// # Returns + /// + /// The final state after processing all members + fn read_struct( + &mut self, + schema: &dyn Schema, + state: T, + consumer: F, + ) -> Result + where + F: FnMut(T, &dyn Schema, &mut Self) -> Result; + + /// Reads a list from the deserializer. + /// + /// The list deserialization is driven by a consumer callback that is called + /// for each element. The consumer receives the current state and the deserializer, + /// and returns the updated state. + /// + /// # Arguments + /// + /// * `schema` - The schema of the list being deserialized + /// * `state` - Initial state (typically a Vec or collection) + /// * `consumer` - Callback invoked for each element with (state, deserializer) + /// + /// # Returns + /// + /// The final state after processing all elements + fn read_list( + &mut self, + schema: &dyn Schema, + state: T, + consumer: F, + ) -> Result + where + F: FnMut(T, &mut Self) -> Result; + + /// Reads a map from the deserializer. + /// + /// The map deserialization is driven by a consumer callback that is called + /// for each entry. The consumer receives the current state, the key, and the + /// deserializer, and returns the updated state. + /// + /// # Arguments + /// + /// * `schema` - The schema of the map being deserialized + /// * `state` - Initial state (typically a HashMap or collection) + /// * `consumer` - Callback invoked for each entry with (state, key, deserializer) + /// + /// # Returns + /// + /// The final state after processing all entries + fn read_map( + &mut self, + schema: &dyn Schema, + state: T, + consumer: F, + ) -> Result + where + F: FnMut(T, String, &mut Self) -> Result; + + /// Reads a boolean value. + fn read_boolean(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a byte (i8) value. + fn read_byte(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a short (i16) value. + fn read_short(&mut self, schema: &dyn Schema) -> Result; + + /// Reads an integer (i32) value. + fn read_integer(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a long (i64) value. + fn read_long(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a float (f32) value. + fn read_float(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a double (f64) value. + fn read_double(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a big integer value. + fn read_big_integer(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a big decimal value. + fn read_big_decimal(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a string value. + fn read_string(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a blob (byte array) value. + fn read_blob(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a timestamp value. + fn read_timestamp(&mut self, schema: &dyn Schema) -> Result; + + /// Reads a document value. + fn read_document(&mut self, schema: &dyn Schema) -> Result; + + /// Checks if the current value is null. + /// + /// This is used for sparse collections where null values are significant. + fn is_null(&mut self) -> bool; + + /// Returns the size of the current container if known. + /// + /// This is an optimization hint that allows pre-allocating collections + /// with the correct capacity. Returns `None` if the size is unknown or + /// not applicable. + fn container_size(&mut self) -> Option; +} diff --git a/rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs b/rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs new file mode 100644 index 00000000000..558de26425f --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs @@ -0,0 +1,159 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Shape serialization interfaces for the Smithy data model. + +use crate::schema::Schema; +use crate::{BigDecimal, BigInteger, Blob, DateTime, Document}; +use std::error::Error; + +/// Serializes Smithy shapes to a target format. +/// +/// This trait provides a format-agnostic API for serializing the Smithy data model. +/// Implementations serialize each data type to the corresponding encoding in their +/// serial format (e.g., Smithy integers and floats to JSON numbers). +/// +/// The serializer accepts a schema along with the value to provide additional +/// information about how to serialize the value (e.g., timestamp format, JSON name). +/// +/// # Type Parameter +/// +/// * `Output` - The serialization target type (e.g., `Vec`, `String`) +/// +/// # Example +/// +/// ```ignore +/// let mut serializer = JsonSerializer::new(); +/// serializer.write_string(&STRING_SCHEMA, "hello")?; +/// let json_bytes = serializer.finish()?; +/// ``` +pub trait ShapeSerializer { + /// The serialization target type (e.g., `Vec`, `String`). + type Output; + + /// The error type returned by serialization operations. + type Error: Error; + + /// Finalizes the serialization and returns the serialized output. + /// + /// This method should be called after all values have been written. + /// It may perform final formatting, validation, or resource cleanup. + fn finish(self) -> Result; + + /// Writes a structure to the serializer. + /// + /// The structure serialization is driven by a callback that writes each member. + /// This avoids the need for trait objects while maintaining flexibility. + /// + /// # Arguments + /// + /// * `schema` - The schema of the structure being serialized + /// * `write_members` - Callback that writes the structure's members + fn write_struct(&mut self, schema: &dyn Schema, write_members: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>; + + /// Writes a list to the serializer. + /// + /// The list serialization is driven by a callback that writes each element. + /// + /// # Arguments + /// + /// * `schema` - The schema of the list being serialized + /// * `write_elements` - Callback that writes the list elements + fn write_list(&mut self, schema: &dyn Schema, write_elements: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>; + + /// Writes a map to the serializer. + /// + /// The map serialization is driven by a callback that writes each entry. + /// + /// # Arguments + /// + /// * `schema` - The schema of the map being serialized + /// * `write_entries` - Callback that writes the map entries + fn write_map(&mut self, schema: &dyn Schema, write_entries: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>; + + /// Writes a boolean value. + fn write_boolean(&mut self, schema: &dyn Schema, value: bool) -> Result<(), Self::Error>; + + /// Writes a byte (i8) value. + fn write_byte(&mut self, schema: &dyn Schema, value: i8) -> Result<(), Self::Error>; + + /// Writes a short (i16) value. + fn write_short(&mut self, schema: &dyn Schema, value: i16) -> Result<(), Self::Error>; + + /// Writes an integer (i32) value. + fn write_integer(&mut self, schema: &dyn Schema, value: i32) -> Result<(), Self::Error>; + + /// Writes a long (i64) value. + fn write_long(&mut self, schema: &dyn Schema, value: i64) -> Result<(), Self::Error>; + + /// Writes a float (f32) value. + fn write_float(&mut self, schema: &dyn Schema, value: f32) -> Result<(), Self::Error>; + + /// Writes a double (f64) value. + fn write_double(&mut self, schema: &dyn Schema, value: f64) -> Result<(), Self::Error>; + + /// Writes a big integer value. + fn write_big_integer( + &mut self, + schema: &dyn Schema, + value: &BigInteger, + ) -> Result<(), Self::Error>; + + /// Writes a big decimal value. + fn write_big_decimal( + &mut self, + schema: &dyn Schema, + value: &BigDecimal, + ) -> Result<(), Self::Error>; + + /// Writes a string value. + fn write_string(&mut self, schema: &dyn Schema, value: &str) -> Result<(), Self::Error>; + + /// Writes a blob (byte array) value. + fn write_blob(&mut self, schema: &dyn Schema, value: &Blob) -> Result<(), Self::Error>; + + /// Writes a timestamp value. + fn write_timestamp(&mut self, schema: &dyn Schema, value: &DateTime) + -> Result<(), Self::Error>; + + /// Writes a document value. + fn write_document(&mut self, schema: &dyn Schema, value: &Document) -> Result<(), Self::Error>; + + /// Writes a null value (for sparse collections). + fn write_null(&mut self, schema: &dyn Schema) -> Result<(), Self::Error>; +} + +/// Trait for structures that can be serialized. +/// +/// This trait is implemented by generated structure types to enable +/// schema-based serialization. +/// +/// # Example +/// +/// ```ignore +/// impl SerializableStruct for MyStruct { +/// fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { +/// serializer.write_struct(&Self::SCHEMA, |ser| { +/// ser.write_string(&FIELD1_SCHEMA, &self.field1)?; +/// ser.write_integer(&FIELD2_SCHEMA, self.field2)?; +/// Ok(()) +/// }) +/// } +/// } +/// ``` +pub trait SerializableStruct { + /// Serializes this structure using the provided serializer. + /// + /// # Arguments + /// + /// * `serializer` - The serializer to write to + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error>; +} From c96e7dfef5c82b6ee1b55a59fbb72ec081a623d2 Mon Sep 17 00:00:00 2001 From: Landon James Date: Thu, 26 Feb 2026 11:07:55 -0800 Subject: [PATCH 4/5] Address PR comments --- rust-runtime/aws-smithy-types/src/schema.rs | 8 +- .../aws-smithy-types/src/schema/prelude.rs | 61 ++++++++----- .../aws-smithy-types/src/schema/serde.rs | 4 +- .../src/schema/serde/deserializer.rs | 4 +- .../aws-smithy-types/src/schema/shape_id.rs | 91 +++++++++++++------ .../aws-smithy-types/src/schema/trait_map.rs | 36 +++----- 6 files changed, 124 insertions(+), 80 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/schema.rs b/rust-runtime/aws-smithy-types/src/schema.rs index 3c3dc1e8296..7f4f9efb784 100644 --- a/rust-runtime/aws-smithy-types/src/schema.rs +++ b/rust-runtime/aws-smithy-types/src/schema.rs @@ -188,16 +188,16 @@ mod test { #[test] fn test_shape_id_parsing() { let id = ShapeId::new("smithy.api#String"); - assert_eq!(id.namespace(), Some("smithy.api")); - assert_eq!(id.shape_name(), Some("String")); + assert_eq!(id.namespace(), "smithy.api"); + assert_eq!(id.shape_name(), "String"); assert_eq!(id.member_name(), None); } #[test] fn test_shape_id_with_member() { let id = ShapeId::new("com.example#MyStruct$member"); - assert_eq!(id.namespace(), Some("com.example")); - assert_eq!(id.shape_name(), Some("MyStruct")); + assert_eq!(id.namespace(), "com.example"); + assert_eq!(id.shape_name(), "MyStruct"); assert_eq!(id.member_name(), Some("member")); } diff --git a/rust-runtime/aws-smithy-types/src/schema/prelude.rs b/rust-runtime/aws-smithy-types/src/schema/prelude.rs index 5bf05a10a61..17dc0df72e0 100644 --- a/rust-runtime/aws-smithy-types/src/schema/prelude.rs +++ b/rust-runtime/aws-smithy-types/src/schema/prelude.rs @@ -8,6 +8,8 @@ //! This module provides const schemas for Smithy's prelude types, //! which are the fundamental types available in all Smithy models. +use std::sync::LazyLock; + use crate::schema::{Schema, ShapeId, ShapeType, TraitMap}; /// A simple schema implementation for prelude types. @@ -34,8 +36,9 @@ impl Schema for PreludeSchema { } fn traits(&self) -> &TraitMap { - static EMPTY_TRAITS: TraitMap = TraitMap::empty(); - &EMPTY_TRAITS + static MAP: LazyLock = LazyLock::new(|| TraitMap::empty()); + + &MAP } } @@ -43,66 +46,80 @@ impl Schema for PreludeSchema { // theoretically cut down on binary size (at the expense of some runtime performance) /// Schema for `smithy.api#String` -pub const STRING: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#String"), ShapeType::String); +pub const STRING: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#String", "smithy.api", "String"), + ShapeType::String, +); /// Schema for `smithy.api#Boolean` pub const BOOLEAN: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#Boolean"), + ShapeId::from_static("smithy.api#Boolean", "smithy.api", "Boolean"), ShapeType::Boolean, ); /// Schema for `smithy.api#Byte` -pub const BYTE: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Byte"), ShapeType::Byte); +pub const BYTE: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Byte", "smithy.api", "Byte"), + ShapeType::Byte, +); /// Schema for `smithy.api#Short` -pub const SHORT: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Short"), ShapeType::Short); +pub const SHORT: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Short", "smithy.api", "Short"), + ShapeType::Short, +); /// Schema for `smithy.api#Integer` pub const INTEGER: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#Integer"), + ShapeId::from_static("smithy.api#Integer", "smithy.api", "Integer"), ShapeType::Integer, ); /// Schema for `smithy.api#Long` -pub const LONG: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Long"), ShapeType::Long); +pub const LONG: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Long", "smithy.api", "Long"), + ShapeType::Long, +); /// Schema for `smithy.api#Float` -pub const FLOAT: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Float"), ShapeType::Float); +pub const FLOAT: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Float", "smithy.api", "Float"), + ShapeType::Float, +); /// Schema for `smithy.api#Double` -pub const DOUBLE: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Double"), ShapeType::Double); +pub const DOUBLE: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Double", "smithy.api", "Double"), + ShapeType::Double, +); /// Schema for `smithy.api#BigInteger` pub const BIG_INTEGER: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#BigInteger"), + ShapeId::from_static("smithy.api#BigInteger", "smithy.api", "BigInteger"), ShapeType::BigInteger, ); /// Schema for `smithy.api#BigDecimal` pub const BIG_DECIMAL: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#BigDecimal"), + ShapeId::from_static("smithy.api#BigDecimal", "smithy.api", "BigDecimal"), ShapeType::BigDecimal, ); /// Schema for `smithy.api#Blob` -pub const BLOB: PreludeSchema = - PreludeSchema::new(ShapeId::from_static("smithy.api#Blob"), ShapeType::Blob); +pub const BLOB: PreludeSchema = PreludeSchema::new( + ShapeId::from_static("smithy.api#Blob", "smithy.api", "Blob"), + ShapeType::Blob, +); /// Schema for `smithy.api#Timestamp` pub const TIMESTAMP: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#Timestamp"), + ShapeId::from_static("smithy.api#Timestamp", "smithy.api", "Timestamp"), ShapeType::Timestamp, ); /// Schema for `smithy.api#Document` pub const DOCUMENT: PreludeSchema = PreludeSchema::new( - ShapeId::from_static("smithy.api#Document"), + ShapeId::from_static("smithy.api#Document", "smithy.api", "Document"), ShapeType::Document, ); diff --git a/rust-runtime/aws-smithy-types/src/schema/serde.rs b/rust-runtime/aws-smithy-types/src/schema/serde.rs index 7d93f00083c..652312b4f10 100644 --- a/rust-runtime/aws-smithy-types/src/schema/serde.rs +++ b/rust-runtime/aws-smithy-types/src/schema/serde.rs @@ -302,11 +302,11 @@ mod test { Ok(crate::Document::Null) } - fn is_null(&mut self) -> bool { + fn is_null(&self) -> bool { false } - fn container_size(&mut self) -> Option { + fn container_size(&self) -> Option { Some(10) } } diff --git a/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs b/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs index 7387916f9f7..9dd12862d96 100644 --- a/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs +++ b/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs @@ -165,12 +165,12 @@ pub trait ShapeDeserializer { /// Checks if the current value is null. /// /// This is used for sparse collections where null values are significant. - fn is_null(&mut self) -> bool; + fn is_null(&self) -> bool; /// Returns the size of the current container if known. /// /// This is an optimization hint that allows pre-allocating collections /// with the correct capacity. Returns `None` if the size is unknown or /// not applicable. - fn container_size(&mut self) -> Option; + fn container_size(&self) -> Option; } diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs index 2ca51c308c0..e647cb1e4b7 100644 --- a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs +++ b/rust-runtime/aws-smithy-types/src/schema/shape_id.rs @@ -3,24 +3,38 @@ * SPDX-License-Identifier: Apache-2.0 */ +use std::borrow::Cow; + /// A Smithy Shape ID. /// /// Shape IDs uniquely identify shapes in a Smithy model. -/// Format: `namespace#shapeName` or `namespace#shapeName$memberName` +/// - `fqn` is `"smithy.example#Foo"` +/// - `namespace` is `"smithy.example"` +/// - `shape_name` is `"Foo"` #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ShapeId { - value: &'static str, + fqn: Cow<'static, str>, + namespace: Cow<'static, str>, + shape_name: Cow<'static, str>, } impl ShapeId { /// Creates a ShapeId from a static string at compile time. /// /// This is used for const initialization of prelude schemas. - pub const fn from_static(value: &'static str) -> Self { - Self { value } + pub const fn from_static( + fqn: &'static str, + namespace: &'static str, + shape_name: &'static str, + ) -> Self { + Self { + fqn: Cow::Borrowed(fqn), + namespace: Cow::Borrowed(namespace), + shape_name: Cow::Borrowed(shape_name), + } } - /// Creates a new ShapeId from a string. + /// Creates a new ShapeId from a namespace and a shape_name. /// /// # Examples /// ``` @@ -28,16 +42,45 @@ impl ShapeId { /// /// let shape_id = ShapeId::new("smithy.api#String"); /// ``` - pub fn new(value: impl Into) -> Self { - // Leak the string to get a 'static reference + pub fn new_from_parts( + namespace: impl Into>, + shape_name: impl Into>, + ) -> Self { + let namespace = namespace.into(); + let shape_name = shape_name.into(); Self { - value: Box::leak(value.into().into_boxed_str()), + fqn: format!("{}#{}", namespace.as_ref(), shape_name.as_ref()).into(), + namespace, + shape_name, } } + /// Creates a new ShapeId from a fully qualified name. + pub fn new_from_fqn(fqn: impl Into>) -> Option { + let fqn = fqn.into(); + let (namespace, shape_name) = fqn.as_ref().split_once('#').map(|(ns, rest)| { + ( + ns.to_string(), + rest.split_once('$') + .map_or(rest, |(name, _)| name) + .to_string(), + ) + })?; + Some(Self { + fqn, + namespace: namespace.into(), + shape_name: shape_name.into(), + }) + } + + /// Creates a new ShapeId from a fully qualified name. + pub fn new(fqn: impl Into>) -> Self { + Self::new_from_fqn(fqn).expect("invalid shape ID") + } + /// Returns the string representation of this ShapeId. pub fn as_str(&self) -> &str { - self.value + self.fqn.as_ref() } /// Returns the namespace portion of the ShapeId. @@ -49,8 +92,8 @@ impl ShapeId { /// let shape_id = ShapeId::from_static("smithy.api#String"); /// assert_eq!(shape_id.namespace(), Some("smithy.api")); /// ``` - pub fn namespace(&self) -> Option<&str> { - self.value.split_once('#').map(|(ns, _)| ns) + pub fn namespace(&self) -> &str { + self.namespace.as_ref() } /// Returns the shape name portion of the ShapeId. @@ -62,10 +105,8 @@ impl ShapeId { /// let shape_id = ShapeId::from_static("smithy.api#String"); /// assert_eq!(shape_id.shape_name(), Some("String")); /// ``` - pub fn shape_name(&self) -> Option<&str> { - self.value - .split_once('#') - .and_then(|(_, rest)| rest.split_once('$').map(|(name, _)| name).or(Some(rest))) + pub fn shape_name(&self) -> &str { + self.shape_name.as_ref() } /// Returns the member name if this is a member shape ID. @@ -78,7 +119,7 @@ impl ShapeId { /// assert_eq!(shape_id.member_name(), Some("member")); /// ``` pub fn member_name(&self) -> Option<&str> { - self.value + self.fqn .split_once('#') .and_then(|(_, rest)| rest.split_once('$').map(|(_, member)| member)) } @@ -92,7 +133,7 @@ impl From for ShapeId { impl From<&str> for ShapeId { fn from(value: &str) -> Self { - Self::new(value) + Self::new(value.to_string()) } } @@ -108,28 +149,20 @@ mod tests { #[test] fn test_namespace() { - assert_eq!( - ShapeId::new("smithy.api#String").namespace(), - Some("smithy.api") - ); + assert_eq!(ShapeId::new("smithy.api#String").namespace(), "smithy.api"); assert_eq!( ShapeId::new("com.example#MyStruct$member").namespace(), - Some("com.example") + "com.example" ); - assert_eq!(ShapeId::new("NoNamespace").namespace(), None); } #[test] fn test_shape_name() { - assert_eq!( - ShapeId::new("smithy.api#String").shape_name(), - Some("String") - ); + assert_eq!(ShapeId::new("smithy.api#String").shape_name(), "String"); assert_eq!( ShapeId::new("com.example#MyStruct$member").shape_name(), - Some("MyStruct") + "MyStruct" ); - assert_eq!(ShapeId::new("NoNamespace").shape_name(), None); } #[test] diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs index 609f03a8cdc..fded7e83d0b 100644 --- a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs +++ b/rust-runtime/aws-smithy-types/src/schema/trait_map.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; /// This provides efficient lookup of traits during serialization and deserialization. #[derive(Debug)] pub struct TraitMap { - traits: Option>>, + traits: HashMap>, } impl Default for TraitMap { @@ -21,55 +21,49 @@ impl Default for TraitMap { } impl TraitMap { + // TODO(schema) Is there a reasonable with_capacity size for this? /// Creates a new empty TraitMap. pub fn new() -> Self { Self { - traits: Some(HashMap::new()), + traits: HashMap::new(), } } - /// Creates an empty TraitMap for const contexts. - pub const fn empty() -> Self { - Self { traits: None } + /// Creates a TraitMap with zero allocated space for Prelude Schemas. + pub(crate) fn empty() -> Self { + Self { + traits: HashMap::with_capacity(0), + } } /// Inserts a trait into the map. pub fn insert(&mut self, trait_obj: Box) { - if self.traits.is_none() { - self.traits = Some(HashMap::new()); - } let id = trait_obj.trait_id().clone(); - self.traits.as_mut().unwrap().insert(id, trait_obj); + self.traits.insert(id, trait_obj); } /// Gets a trait by its Shape ID. pub fn get(&self, id: &ShapeId) -> Option<&dyn Trait> { - self.traits.as_ref()?.get(id).map(|t| t.as_ref()) + self.traits.get(id).map(|t| t.as_ref()) } /// Returns true if the map contains a trait with the given Shape ID. pub fn contains(&self, id: &ShapeId) -> bool { - self.traits - .as_ref() - .map(|m| m.contains_key(id)) - .unwrap_or(false) + self.traits.contains_key(id) } /// Returns an iterator over all traits. - pub fn iter(&self) -> impl Iterator { - self.traits - .as_ref() - .into_iter() - .flat_map(|m| m.values().map(|t| t.as_ref())) + pub fn iter(&self) -> impl Iterator { + self.traits.iter().map(|(s, t)| (s, t.as_ref())) } /// Returns the number of traits in the map. pub fn len(&self) -> usize { - self.traits.as_ref().map(|m| m.len()).unwrap_or(0) + self.traits.len() } /// Returns true if the map is empty. pub fn is_empty(&self) -> bool { - self.traits.as_ref().map(|m| m.is_empty()).unwrap_or(true) + self.traits.is_empty() } } From bcb15a1f8b102ab3e59dfc703dfbe8602636364b Mon Sep 17 00:00:00 2001 From: Landon James Date: Thu, 26 Feb 2026 11:27:27 -0800 Subject: [PATCH 5/5] Move Schema types to aws-smithy-schema Add aws-smithy-schema to CrateSet --- buildSrc/src/main/kotlin/CrateSet.kt | 1 + rust-runtime/Cargo.lock | 7 + rust-runtime/Cargo.toml | 17 +- rust-runtime/aws-smithy-schema/Cargo.toml | 21 +++ rust-runtime/aws-smithy-schema/LICENSE | 175 ++++++++++++++++++ rust-runtime/aws-smithy-schema/README.md | 7 + .../src/lib.rs} | 36 ++-- .../src/schema/prelude.rs | 6 +- .../src/schema/serde.rs | 43 +++-- .../src/schema/serde/deserializer.rs | 4 +- .../src/schema/serde/serializer.rs | 4 +- .../src/schema/shape_id.rs | 17 +- .../src/schema/shape_type.rs | 0 .../src/schema/trait_map.rs | 2 +- .../src/schema/trait_type.rs | 2 +- rust-runtime/aws-smithy-types/src/lib.rs | 2 - 16 files changed, 288 insertions(+), 56 deletions(-) create mode 100644 rust-runtime/aws-smithy-schema/Cargo.toml create mode 100644 rust-runtime/aws-smithy-schema/LICENSE create mode 100644 rust-runtime/aws-smithy-schema/README.md rename rust-runtime/{aws-smithy-types/src/schema.rs => aws-smithy-schema/src/lib.rs} (91%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/prelude.rs (97%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/serde.rs (90%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/serde/deserializer.rs (98%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/serde/serializer.rs (98%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/shape_id.rs (90%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/shape_type.rs (100%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/trait_map.rs (98%) rename rust-runtime/{aws-smithy-types => aws-smithy-schema}/src/schema/trait_type.rs (96%) diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index a5ba336067e..b52eb68798b 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -72,6 +72,7 @@ object CrateSet { "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-smithy-types-convert", "aws-smithy-wasm", diff --git a/rust-runtime/Cargo.lock b/rust-runtime/Cargo.lock index e218a5bfcf5..8c4920c9a25 100644 --- a/rust-runtime/Cargo.lock +++ b/rust-runtime/Cargo.lock @@ -764,6 +764,13 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-smithy-schema" +version = "0.1.0" +dependencies = [ + "aws-smithy-types", +] + [[package]] name = "aws-smithy-types" version = "1.4.5" diff --git a/rust-runtime/Cargo.toml b/rust-runtime/Cargo.toml index cc9de5df724..d052d413b1a 100644 --- a/rust-runtime/Cargo.toml +++ b/rust-runtime/Cargo.toml @@ -8,25 +8,26 @@ members = [ "aws-smithy-compression", "aws-smithy-dns", "aws-smithy-eventstream", + "aws-smithy-experimental", "aws-smithy-http", "aws-smithy-http-client", "aws-smithy-http-server", - "aws-smithy-legacy-http", - "aws-smithy-legacy-http-server", + "aws-smithy-http-server-metrics", + "aws-smithy-http-server-metrics-macro", "aws-smithy-http-server-python", "aws-smithy-json", + "aws-smithy-legacy-http", + "aws-smithy-legacy-http-server", + "aws-smithy-mocks", + "aws-smithy-observability", + "aws-smithy-observability-otel", "aws-smithy-protocol-test", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-smithy-types-convert", "aws-smithy-wasm", - "aws-smithy-mocks", - "aws-smithy-experimental", "aws-smithy-xml", - "aws-smithy-observability", - "aws-smithy-observability-otel", - "aws-smithy-http-server-metrics", - "aws-smithy-http-server-metrics-macro", ] diff --git a/rust-runtime/aws-smithy-schema/Cargo.toml b/rust-runtime/aws-smithy-schema/Cargo.toml new file mode 100644 index 00000000000..8bd0f2df8d3 --- /dev/null +++ b/rust-runtime/aws-smithy-schema/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aws-smithy-schema" +version = "0.1.0" +authors = ["AWS Rust SDK Team "] +description = "Schema types for the smithy-rs ecosystem" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/smithy-lang/smithy-rs" +rust-version = "1.91" + +[dependencies] +aws-smithy-types = { path = "../aws-smithy-types", default-features = false } + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/rust-runtime/aws-smithy-schema/LICENSE b/rust-runtime/aws-smithy-schema/LICENSE new file mode 100644 index 00000000000..67db8588217 --- /dev/null +++ b/rust-runtime/aws-smithy-schema/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/rust-runtime/aws-smithy-schema/README.md b/rust-runtime/aws-smithy-schema/README.md new file mode 100644 index 00000000000..622290ca002 --- /dev/null +++ b/rust-runtime/aws-smithy-schema/README.md @@ -0,0 +1,7 @@ +# aws-smithy-schema + +Schema types for the smithy-rs ecosystem. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/smithy-lang/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/rust-runtime/aws-smithy-types/src/schema.rs b/rust-runtime/aws-smithy-schema/src/lib.rs similarity index 91% rename from rust-runtime/aws-smithy-types/src/schema.rs rename to rust-runtime/aws-smithy-schema/src/lib.rs index 7f4f9efb784..98527f08b0f 100644 --- a/rust-runtime/aws-smithy-types/src/schema.rs +++ b/rust-runtime/aws-smithy-schema/src/lib.rs @@ -3,23 +3,37 @@ * SPDX-License-Identifier: Apache-2.0 */ +/* Automatically managed default lints */ +#![cfg_attr(docsrs, feature(doc_cfg))] +/* End of automatically managed default lints */ + //! Runtime schema types for Smithy shapes. //! //! This module provides the core types for representing Smithy schemas at runtime, //! enabling protocol-agnostic serialization and deserialization. -mod shape_id; -mod shape_type; -mod trait_map; -mod trait_type; +mod schema { + pub mod shape_id; + pub mod shape_type; + pub mod trait_map; + pub mod trait_type; + + pub mod prelude; + pub mod serde; +} + +pub use schema::shape_id::ShapeId; +pub use schema::shape_type::ShapeType; +pub use schema::trait_map::TraitMap; +pub use schema::trait_type::Trait; -pub mod prelude; -pub mod serde; +pub mod prelude { + pub use crate::schema::prelude::*; +} -pub use shape_id::ShapeId; -pub use shape_type::ShapeType; -pub use trait_map::TraitMap; -pub use trait_type::Trait; +pub mod serde { + pub use crate::schema::serde::*; +} /// Core trait representing a Smithy schema at runtime. /// @@ -119,7 +133,7 @@ impl SchemaExt for T {} #[cfg(test)] mod test { - use crate::schema::{Schema, SchemaExt, ShapeId, ShapeType, Trait, TraitMap}; + use crate::{Schema, SchemaExt, ShapeId, ShapeType, Trait, TraitMap}; // Simple test trait implementation #[derive(Debug)] diff --git a/rust-runtime/aws-smithy-types/src/schema/prelude.rs b/rust-runtime/aws-smithy-schema/src/schema/prelude.rs similarity index 97% rename from rust-runtime/aws-smithy-types/src/schema/prelude.rs rename to rust-runtime/aws-smithy-schema/src/schema/prelude.rs index 17dc0df72e0..d0de6b01673 100644 --- a/rust-runtime/aws-smithy-types/src/schema/prelude.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/prelude.rs @@ -10,7 +10,7 @@ use std::sync::LazyLock; -use crate::schema::{Schema, ShapeId, ShapeType, TraitMap}; +use crate::{Schema, ShapeId, ShapeType, TraitMap}; /// A simple schema implementation for prelude types. #[derive(Debug)] @@ -36,7 +36,7 @@ impl Schema for PreludeSchema { } fn traits(&self) -> &TraitMap { - static MAP: LazyLock = LazyLock::new(|| TraitMap::empty()); + static MAP: LazyLock = LazyLock::new(TraitMap::empty); &MAP } @@ -126,7 +126,7 @@ pub const DOCUMENT: PreludeSchema = PreludeSchema::new( #[cfg(test)] mod tests { use super::*; - use crate::schema::SchemaExt; + use crate::SchemaExt; #[test] fn test_string_schema() { diff --git a/rust-runtime/aws-smithy-types/src/schema/serde.rs b/rust-runtime/aws-smithy-schema/src/schema/serde.rs similarity index 90% rename from rust-runtime/aws-smithy-types/src/schema/serde.rs rename to rust-runtime/aws-smithy-schema/src/schema/serde.rs index 652312b4f10..4a3f199409e 100644 --- a/rust-runtime/aws-smithy-types/src/schema/serde.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/serde.rs @@ -13,8 +13,8 @@ pub use serializer::{SerializableStruct, ShapeSerializer}; #[cfg(test)] mod test { - use crate::schema::serde::{ShapeDeserializer, ShapeSerializer}; - use crate::schema::{prelude::*, Schema}; + use crate::serde::{ShapeDeserializer, ShapeSerializer}; + use crate::{prelude::*, Schema}; use std::fmt; // Mock error type for testing @@ -121,7 +121,7 @@ mod test { fn write_big_integer( &mut self, _schema: &dyn Schema, - value: &crate::BigInteger, + value: &aws_smithy_types::BigInteger, ) -> Result<(), Self::Error> { self.output.push(format!("bigint({})", value.as_ref())); Ok(()) @@ -130,7 +130,7 @@ mod test { fn write_big_decimal( &mut self, _schema: &dyn Schema, - value: &crate::BigDecimal, + value: &aws_smithy_types::BigDecimal, ) -> Result<(), Self::Error> { self.output.push(format!("bigdec({})", value.as_ref())); Ok(()) @@ -144,7 +144,7 @@ mod test { fn write_blob( &mut self, _schema: &dyn Schema, - value: &crate::Blob, + value: &aws_smithy_types::Blob, ) -> Result<(), Self::Error> { self.output .push(format!("blob({} bytes)", value.as_ref().len())); @@ -154,7 +154,7 @@ mod test { fn write_timestamp( &mut self, _schema: &dyn Schema, - value: &crate::DateTime, + value: &aws_smithy_types::DateTime, ) -> Result<(), Self::Error> { self.output.push(format!("timestamp({})", value)); Ok(()) @@ -163,7 +163,7 @@ mod test { fn write_document( &mut self, _schema: &dyn Schema, - _value: &crate::Document, + _value: &aws_smithy_types::Document, ) -> Result<(), Self::Error> { self.output.push("document".to_string()); Ok(()) @@ -267,17 +267,17 @@ mod test { fn read_big_integer( &mut self, _schema: &dyn Schema, - ) -> Result { + ) -> Result { use std::str::FromStr; - Ok(crate::BigInteger::from_str("12345").unwrap()) + Ok(aws_smithy_types::BigInteger::from_str("12345").unwrap()) } fn read_big_decimal( &mut self, _schema: &dyn Schema, - ) -> Result { + ) -> Result { use std::str::FromStr; - Ok(crate::BigDecimal::from_str("123.45").unwrap()) + Ok(aws_smithy_types::BigDecimal::from_str("123.45").unwrap()) } fn read_string(&mut self, _schema: &dyn Schema) -> Result { @@ -290,16 +290,25 @@ mod test { } } - fn read_blob(&mut self, _schema: &dyn Schema) -> Result { - Ok(crate::Blob::new(vec![1, 2, 3, 4])) + fn read_blob( + &mut self, + _schema: &dyn Schema, + ) -> Result { + Ok(aws_smithy_types::Blob::new(vec![1, 2, 3, 4])) } - fn read_timestamp(&mut self, _schema: &dyn Schema) -> Result { - Ok(crate::DateTime::from_secs(1234567890)) + fn read_timestamp( + &mut self, + _schema: &dyn Schema, + ) -> Result { + Ok(aws_smithy_types::DateTime::from_secs(1234567890)) } - fn read_document(&mut self, _schema: &dyn Schema) -> Result { - Ok(crate::Document::Null) + fn read_document( + &mut self, + _schema: &dyn Schema, + ) -> Result { + Ok(aws_smithy_types::Document::Null) } fn is_null(&self) -> bool { diff --git a/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs b/rust-runtime/aws-smithy-schema/src/schema/serde/deserializer.rs similarity index 98% rename from rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs rename to rust-runtime/aws-smithy-schema/src/schema/serde/deserializer.rs index 9dd12862d96..a034bde1198 100644 --- a/rust-runtime/aws-smithy-types/src/schema/serde/deserializer.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/serde/deserializer.rs @@ -5,8 +5,8 @@ //! Shape deserialization interfaces for the Smithy data model. -use crate::schema::Schema; -use crate::{BigDecimal, BigInteger, Blob, DateTime, Document}; +use crate::Schema; +use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document}; use std::error::Error; /// Deserializes Smithy shapes from a serial format. diff --git a/rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs b/rust-runtime/aws-smithy-schema/src/schema/serde/serializer.rs similarity index 98% rename from rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs rename to rust-runtime/aws-smithy-schema/src/schema/serde/serializer.rs index 558de26425f..add0e3c56e0 100644 --- a/rust-runtime/aws-smithy-types/src/schema/serde/serializer.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/serde/serializer.rs @@ -5,8 +5,8 @@ //! Shape serialization interfaces for the Smithy data model. -use crate::schema::Schema; -use crate::{BigDecimal, BigInteger, Blob, DateTime, Document}; +use crate::Schema; +use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document}; use std::error::Error; /// Serializes Smithy shapes to a target format. diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs b/rust-runtime/aws-smithy-schema/src/schema/shape_id.rs similarity index 90% rename from rust-runtime/aws-smithy-types/src/schema/shape_id.rs rename to rust-runtime/aws-smithy-schema/src/schema/shape_id.rs index e647cb1e4b7..5688b4b404e 100644 --- a/rust-runtime/aws-smithy-types/src/schema/shape_id.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/shape_id.rs @@ -38,7 +38,7 @@ impl ShapeId { /// /// # Examples /// ``` - /// use aws_smithy_types::schema::ShapeId; + /// use aws_smithy_schema::ShapeId; /// /// let shape_id = ShapeId::new("smithy.api#String"); /// ``` @@ -87,10 +87,10 @@ impl ShapeId { /// /// # Examples /// ``` - /// use aws_smithy_types::schema::ShapeId; + /// use aws_smithy_schema::ShapeId; /// - /// let shape_id = ShapeId::from_static("smithy.api#String"); - /// assert_eq!(shape_id.namespace(), Some("smithy.api")); + /// let shape_id = ShapeId::new("smithy.api#String"); + /// assert_eq!(shape_id.namespace(), "smithy.api"); /// ``` pub fn namespace(&self) -> &str { self.namespace.as_ref() @@ -100,10 +100,10 @@ impl ShapeId { /// /// # Examples /// ``` - /// use aws_smithy_types::schema::ShapeId; + /// use aws_smithy_schema::ShapeId; /// - /// let shape_id = ShapeId::from_static("smithy.api#String"); - /// assert_eq!(shape_id.shape_name(), Some("String")); + /// let shape_id = ShapeId::new("smithy.api#String"); + /// assert_eq!(shape_id.shape_name(), "String"); /// ``` pub fn shape_name(&self) -> &str { self.shape_name.as_ref() @@ -113,7 +113,7 @@ impl ShapeId { /// /// # Examples /// ``` - /// use aws_smithy_types::schema::ShapeId; + /// use aws_smithy_schema::ShapeId; /// /// let shape_id = ShapeId::new("com.example#MyStruct$member"); /// assert_eq!(shape_id.member_name(), Some("member")); @@ -172,7 +172,6 @@ mod tests { Some("member") ); assert_eq!(ShapeId::new("smithy.api#String").member_name(), None); - assert_eq!(ShapeId::new("NoNamespace").member_name(), None); } #[test] diff --git a/rust-runtime/aws-smithy-types/src/schema/shape_type.rs b/rust-runtime/aws-smithy-schema/src/schema/shape_type.rs similarity index 100% rename from rust-runtime/aws-smithy-types/src/schema/shape_type.rs rename to rust-runtime/aws-smithy-schema/src/schema/shape_type.rs diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs b/rust-runtime/aws-smithy-schema/src/schema/trait_map.rs similarity index 98% rename from rust-runtime/aws-smithy-types/src/schema/trait_map.rs rename to rust-runtime/aws-smithy-schema/src/schema/trait_map.rs index fded7e83d0b..555f9b2bb8d 100644 --- a/rust-runtime/aws-smithy-types/src/schema/trait_map.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/trait_map.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{ShapeId, Trait}; +use crate::{ShapeId, Trait}; use std::collections::HashMap; /// A map of traits keyed by their Shape ID. diff --git a/rust-runtime/aws-smithy-types/src/schema/trait_type.rs b/rust-runtime/aws-smithy-schema/src/schema/trait_type.rs similarity index 96% rename from rust-runtime/aws-smithy-types/src/schema/trait_type.rs rename to rust-runtime/aws-smithy-schema/src/schema/trait_type.rs index 56bc1993dbb..412d3f399f3 100644 --- a/rust-runtime/aws-smithy-types/src/schema/trait_type.rs +++ b/rust-runtime/aws-smithy-schema/src/schema/trait_type.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::ShapeId; +use crate::ShapeId; use std::any::Any; use std::fmt; diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 9f99d3c88ab..6f893b70a1e 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -30,8 +30,6 @@ pub mod error; pub mod event_stream; pub mod primitive; pub mod retry; -/// Runtime schema types for Smithy shapes. -pub mod schema; pub mod timeout; /// Utilities for type erasure.