AWS SDK

AWS SDK

rev. 96f5a1b4ad139d2f1ad1e8e40f300e1cd1ff574c

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/http_protocol/rpc.rs

@@ -0,1 +0,425 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! HTTP RPC protocol for body-only APIs.
           7  +
           8  +
use crate::codec::{Codec, FinishSerializer};
           9  +
use crate::protocol::ClientProtocol;
          10  +
use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
          11  +
use crate::{Schema, ShapeId};
          12  +
use aws_smithy_runtime_api::http::{Request, Response};
          13  +
use aws_smithy_types::body::SdkBody;
          14  +
use aws_smithy_types::config_bag::ConfigBag;
          15  +
          16  +
/// An HTTP protocol for RPC-style APIs that put everything in the body.
          17  +
///
          18  +
/// This protocol ignores HTTP binding traits and serializes the entire input
          19  +
/// into the request body using the provided codec. Used by protocols like
          20  +
/// `awsJson1_0`, `awsJson1_1`, and `rpcv2Cbor`.
          21  +
///
          22  +
/// # Type parameters
          23  +
///
          24  +
/// * `C` — the payload codec (ex: `JsonCodec`, `CborCodec`)
          25  +
#[derive(Debug)]
          26  +
pub struct HttpRpcProtocol<C> {
          27  +
    protocol_id: ShapeId,
          28  +
    codec: C,
          29  +
    content_type: &'static str,
          30  +
}
          31  +
          32  +
impl<C: Codec> HttpRpcProtocol<C> {
          33  +
    /// Creates a new HTTP RPC protocol.
          34  +
    pub fn new(protocol_id: ShapeId, codec: C, content_type: &'static str) -> Self {
          35  +
        Self {
          36  +
            protocol_id,
          37  +
            codec,
          38  +
            content_type,
          39  +
        }
          40  +
    }
          41  +
}
          42  +
          43  +
impl<C> ClientProtocol for HttpRpcProtocol<C>
          44  +
where
          45  +
    C: Codec + Send + Sync + std::fmt::Debug + 'static,
          46  +
    for<'a> C::Deserializer<'a>: ShapeDeserializer,
          47  +
{
          48  +
    fn protocol_id(&self) -> &ShapeId {
          49  +
        &self.protocol_id
          50  +
    }
          51  +
          52  +
    fn serialize_request(
          53  +
        &self,
          54  +
        input: &dyn SerializableStruct,
          55  +
        input_schema: &Schema,
          56  +
        endpoint: &str,
          57  +
        _cfg: &ConfigBag,
          58  +
    ) -> Result<Request, SerdeError> {
          59  +
        let mut serializer = self.codec.create_serializer();
          60  +
        serializer.write_struct(input_schema, input)?;
          61  +
        let body = serializer.finish();
          62  +
          63  +
        let mut request = Request::new(SdkBody::from(body));
          64  +
        request
          65  +
            .set_method("POST")
          66  +
            .map_err(|e| SerdeError::custom(format!("invalid HTTP method: {e}")))?;
          67  +
        let uri = if endpoint.is_empty() { "/" } else { endpoint };
          68  +
        request
          69  +
            .set_uri(uri)
          70  +
            .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
          71  +
        request
          72  +
            .headers_mut()
          73  +
            .insert("Content-Type", self.content_type);
          74  +
        if let Some(len) = request.body().content_length() {
          75  +
            request
          76  +
                .headers_mut()
          77  +
                .insert("Content-Length", len.to_string());
          78  +
        }
          79  +
        Ok(request)
          80  +
    }
          81  +
          82  +
    fn deserialize_response<'a>(
          83  +
        &self,
          84  +
        response: &'a Response,
          85  +
        _output_schema: &Schema,
          86  +
        _cfg: &ConfigBag,
          87  +
    ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
          88  +
        let body = response
          89  +
            .body()
          90  +
            .bytes()
          91  +
            .ok_or_else(|| SerdeError::custom("response body is not available as bytes"))?;
          92  +
        Ok(Box::new(self.codec.create_deserializer(body)))
          93  +
    }
          94  +
}
          95  +
          96  +
#[cfg(test)]
          97  +
mod tests {
          98  +
    use super::*;
          99  +
    use crate::serde::SerializableStruct;
         100  +
    use crate::{prelude::*, ShapeType};
         101  +
         102  +
    struct TestSerializer {
         103  +
        output: Vec<u8>,
         104  +
    }
         105  +
         106  +
    impl FinishSerializer for TestSerializer {
         107  +
        fn finish(self) -> Vec<u8> {
         108  +
            self.output
         109  +
        }
         110  +
    }
         111  +
         112  +
    impl ShapeSerializer for TestSerializer {
         113  +
        fn write_struct(
         114  +
            &mut self,
         115  +
            _: &Schema,
         116  +
            value: &dyn SerializableStruct,
         117  +
        ) -> Result<(), SerdeError> {
         118  +
            self.output.push(b'{');
         119  +
            value.serialize_members(self)?;
         120  +
            self.output.push(b'}');
         121  +
            Ok(())
         122  +
        }
         123  +
        fn write_list(
         124  +
            &mut self,
         125  +
            _: &Schema,
         126  +
            _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
         127  +
        ) -> Result<(), SerdeError> {
         128  +
            Ok(())
         129  +
        }
         130  +
        fn write_map(
         131  +
            &mut self,
         132  +
            _: &Schema,
         133  +
            _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
         134  +
        ) -> Result<(), SerdeError> {
         135  +
            Ok(())
         136  +
        }
         137  +
        fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
         138  +
            Ok(())
         139  +
        }
         140  +
        fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
         141  +
            Ok(())
         142  +
        }
         143  +
        fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
         144  +
            Ok(())
         145  +
        }
         146  +
        fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
         147  +
            Ok(())
         148  +
        }
         149  +
        fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
         150  +
            Ok(())
         151  +
        }
         152  +
        fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
         153  +
            Ok(())
         154  +
        }
         155  +
        fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
         156  +
            Ok(())
         157  +
        }
         158  +
        fn write_big_integer(
         159  +
            &mut self,
         160  +
            _: &Schema,
         161  +
            _: &aws_smithy_types::BigInteger,
         162  +
        ) -> Result<(), SerdeError> {
         163  +
            Ok(())
         164  +
        }
         165  +
        fn write_big_decimal(
         166  +
            &mut self,
         167  +
            _: &Schema,
         168  +
            _: &aws_smithy_types::BigDecimal,
         169  +
        ) -> Result<(), SerdeError> {
         170  +
            Ok(())
         171  +
        }
         172  +
        fn write_string(&mut self, _: &Schema, v: &str) -> Result<(), SerdeError> {
         173  +
            self.output.extend_from_slice(v.as_bytes());
         174  +
            Ok(())
         175  +
        }
         176  +
        fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
         177  +
            Ok(())
         178  +
        }
         179  +
        fn write_timestamp(
         180  +
            &mut self,
         181  +
            _: &Schema,
         182  +
            _: &aws_smithy_types::DateTime,
         183  +
        ) -> Result<(), SerdeError> {
         184  +
            Ok(())
         185  +
        }
         186  +
        fn write_document(
         187  +
            &mut self,
         188  +
            _: &Schema,
         189  +
            _: &aws_smithy_types::Document,
         190  +
        ) -> Result<(), SerdeError> {
         191  +
            Ok(())
         192  +
        }
         193  +
        fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
         194  +
            Ok(())
         195  +
        }
         196  +
    }
         197  +
         198  +
    struct TestDeserializer<'a> {
         199  +
        input: &'a [u8],
         200  +
    }
         201  +
         202  +
    impl ShapeDeserializer for TestDeserializer<'_> {
         203  +
        fn read_struct(
         204  +
            &mut self,
         205  +
            _: &Schema,
         206  +
            _: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         207  +
        ) -> Result<(), SerdeError> {
         208  +
            Ok(())
         209  +
        }
         210  +
        fn read_list(
         211  +
            &mut self,
         212  +
            _: &Schema,
         213  +
            _: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         214  +
        ) -> Result<(), SerdeError> {
         215  +
            Ok(())
         216  +
        }
         217  +
        fn read_map(
         218  +
            &mut self,
         219  +
            _: &Schema,
         220  +
            _: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         221  +
        ) -> Result<(), SerdeError> {
         222  +
            Ok(())
         223  +
        }
         224  +
        fn read_boolean(&mut self, _: &Schema) -> Result<bool, SerdeError> {
         225  +
            Ok(false)
         226  +
        }
         227  +
        fn read_byte(&mut self, _: &Schema) -> Result<i8, SerdeError> {
         228  +
            Ok(0)
         229  +
        }
         230  +
        fn read_short(&mut self, _: &Schema) -> Result<i16, SerdeError> {
         231  +
            Ok(0)
         232  +
        }
         233  +
        fn read_integer(&mut self, _: &Schema) -> Result<i32, SerdeError> {
         234  +
            Ok(0)
         235  +
        }
         236  +
        fn read_long(&mut self, _: &Schema) -> Result<i64, SerdeError> {
         237  +
            Ok(0)
         238  +
        }
         239  +
        fn read_float(&mut self, _: &Schema) -> Result<f32, SerdeError> {
         240  +
            Ok(0.0)
         241  +
        }
         242  +
        fn read_double(&mut self, _: &Schema) -> Result<f64, SerdeError> {
         243  +
            Ok(0.0)
         244  +
        }
         245  +
        fn read_big_integer(
         246  +
            &mut self,
         247  +
            _: &Schema,
         248  +
        ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
         249  +
            use std::str::FromStr;
         250  +
            Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
         251  +
        }
         252  +
        fn read_big_decimal(
         253  +
            &mut self,
         254  +
            _: &Schema,
         255  +
        ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
         256  +
            use std::str::FromStr;
         257  +
            Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
         258  +
        }
         259  +
        fn read_string(&mut self, _: &Schema) -> Result<String, SerdeError> {
         260  +
            Ok(String::from_utf8_lossy(self.input).into_owned())
         261  +
        }
         262  +
        fn read_blob(&mut self, _: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
         263  +
            Ok(aws_smithy_types::Blob::new(vec![]))
         264  +
        }
         265  +
        fn read_timestamp(&mut self, _: &Schema) -> Result<aws_smithy_types::DateTime, SerdeError> {
         266  +
            Ok(aws_smithy_types::DateTime::from_secs(0))
         267  +
        }
         268  +
        fn read_document(&mut self, _: &Schema) -> Result<aws_smithy_types::Document, SerdeError> {
         269  +
            Ok(aws_smithy_types::Document::Null)
         270  +
        }
         271  +
        fn is_null(&self) -> bool {
         272  +
            false
         273  +
        }
         274  +
        fn container_size(&self) -> Option<usize> {
         275  +
            None
         276  +
        }
         277  +
    }
         278  +
         279  +
    #[derive(Debug)]
         280  +
    struct TestCodec;
         281  +
         282  +
    impl Codec for TestCodec {
         283  +
        type Serializer = TestSerializer;
         284  +
        type Deserializer<'a> = TestDeserializer<'a>;
         285  +
        fn create_serializer(&self) -> Self::Serializer {
         286  +
            TestSerializer { output: Vec::new() }
         287  +
        }
         288  +
        fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
         289  +
            TestDeserializer { input }
         290  +
        }
         291  +
    }
         292  +
         293  +
    static TEST_SCHEMA: Schema =
         294  +
        Schema::new(crate::shape_id!("test", "TestStruct"), ShapeType::Structure);
         295  +
         296  +
    struct EmptyStruct;
         297  +
    impl SerializableStruct for EmptyStruct {
         298  +
        fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
         299  +
            Ok(())
         300  +
        }
         301  +
    }
         302  +
         303  +
    static NAME_MEMBER: Schema = Schema::new_member(
         304  +
        crate::shape_id!("test", "TestStruct"),
         305  +
        ShapeType::String,
         306  +
        "name",
         307  +
        0,
         308  +
    );
         309  +
    static MEMBERS: &[&Schema] = &[&NAME_MEMBER];
         310  +
    static STRUCT_WITH_MEMBER: Schema = Schema::new_struct(
         311  +
        crate::shape_id!("test", "TestStruct"),
         312  +
        ShapeType::Structure,
         313  +
        MEMBERS,
         314  +
    );
         315  +
         316  +
    struct NameStruct;
         317  +
    impl SerializableStruct for NameStruct {
         318  +
        fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
         319  +
            s.write_string(&NAME_MEMBER, "Alice")
         320  +
        }
         321  +
    }
         322  +
         323  +
    #[test]
         324  +
    fn serialize_sets_content_type() {
         325  +
        let protocol = HttpRpcProtocol::new(
         326  +
            crate::shape_id!("test", "rpc"),
         327  +
            TestCodec,
         328  +
            "application/x-amz-json-1.0",
         329  +
        );
         330  +
        let request = protocol
         331  +
            .serialize_request(
         332  +
                &EmptyStruct,
         333  +
                &TEST_SCHEMA,
         334  +
                "https://example.com",
         335  +
                &ConfigBag::base(),
         336  +
            )
         337  +
            .unwrap();
         338  +
        assert_eq!(
         339  +
            request.headers().get("Content-Type").unwrap(),
         340  +
            "application/x-amz-json-1.0"
         341  +
        );
         342  +
    }
         343  +
         344  +
    #[test]
         345  +
    fn serialize_body() {
         346  +
        let protocol = HttpRpcProtocol::new(
         347  +
            crate::shape_id!("test", "rpc"),
         348  +
            TestCodec,
         349  +
            "application/x-amz-json-1.0",
         350  +
        );
         351  +
        let request = protocol
         352  +
            .serialize_request(
         353  +
                &NameStruct,
         354  +
                &STRUCT_WITH_MEMBER,
         355  +
                "https://example.com",
         356  +
                &ConfigBag::base(),
         357  +
            )
         358  +
            .unwrap();
         359  +
        assert_eq!(request.body().bytes().unwrap(), b"{Alice}");
         360  +
    }
         361  +
         362  +
    #[test]
         363  +
    fn serialize_empty_endpoint_defaults_to_root() {
         364  +
        let protocol = HttpRpcProtocol::new(
         365  +
            crate::shape_id!("test", "rpc"),
         366  +
            TestCodec,
         367  +
            "application/x-amz-json-1.0",
         368  +
        );
         369  +
        let request = protocol
         370  +
            .serialize_request(&EmptyStruct, &TEST_SCHEMA, "", &ConfigBag::base())
         371  +
            .unwrap();
         372  +
        assert_eq!(request.uri(), "/");
         373  +
    }
         374  +
         375  +
    #[test]
         376  +
    fn deserialize_response() {
         377  +
        let protocol = HttpRpcProtocol::new(
         378  +
            crate::shape_id!("test", "rpc"),
         379  +
            TestCodec,
         380  +
            "application/x-amz-json-1.0",
         381  +
        );
         382  +
        let response = Response::new(
         383  +
            200u16.try_into().unwrap(),
         384  +
            SdkBody::from(r#"{"result":42}"#),
         385  +
        );
         386  +
        let mut deser = protocol
         387  +
            .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
         388  +
            .unwrap();
         389  +
        assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"result":42}"#);
         390  +
    }
         391  +
         392  +
    #[test]
         393  +
    fn update_endpoint() {
         394  +
        let protocol = HttpRpcProtocol::new(
         395  +
            crate::shape_id!("test", "rpc"),
         396  +
            TestCodec,
         397  +
            "application/x-amz-json-1.0",
         398  +
        );
         399  +
        let mut request = protocol
         400  +
            .serialize_request(
         401  +
                &EmptyStruct,
         402  +
                &TEST_SCHEMA,
         403  +
                "https://old.example.com",
         404  +
                &ConfigBag::base(),
         405  +
            )
         406  +
            .unwrap();
         407  +
        let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
         408  +
            .url("https://new.example.com")
         409  +
            .build();
         410  +
        protocol
         411  +
            .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
         412  +
            .unwrap();
         413  +
        assert_eq!(request.uri(), "https://new.example.com/");
         414  +
    }
         415  +
         416  +
    #[test]
         417  +
    fn protocol_id() {
         418  +
        let protocol = HttpRpcProtocol::new(
         419  +
            crate::shape_id!("aws.protocols", "awsJson1_0"),
         420  +
            TestCodec,
         421  +
            "application/x-amz-json-1.0",
         422  +
        );
         423  +
        assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#awsJson1_0");
         424  +
    }
         425  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/protocol.rs

@@ -0,1 +0,309 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! Client protocol trait for protocol-agnostic request serialization and response deserialization.
           7  +
//!
           8  +
//! A [`ClientProtocol`] uses a combination of codecs, serializers, and deserializers to
           9  +
//! serialize requests and deserialize responses for a specific Smithy protocol
          10  +
//! (e.g., AWS JSON 1.0, REST JSON, REST XML, RPCv2 CBOR).
          11  +
//!
          12  +
//! # Implementing a custom protocol
          13  +
//!
          14  +
//! Third parties can create custom protocols and use them with any client without
          15  +
//! modifying a code generator.
          16  +
//!
          17  +
//! ```ignore
          18  +
//! use aws_smithy_schema::protocol::ClientProtocol;
          19  +
//! use aws_smithy_schema::{Schema, ShapeId};
          20  +
//! use aws_smithy_schema::serde::SerializableStruct;
          21  +
//!
          22  +
//! #[derive(Debug)]
          23  +
//! struct MyProtocol {
          24  +
//!     codec: MyJsonCodec,
          25  +
//! }
          26  +
//!
          27  +
//! impl ClientProtocol for MyProtocol {
          28  +
//!     fn protocol_id(&self) -> &ShapeId { &MY_PROTOCOL_ID }
          29  +
//!
          30  +
//!     fn serialize_request(
          31  +
//!         &self,
          32  +
//!         input: &dyn SerializableStruct,
          33  +
//!         input_schema: &Schema,
          34  +
//!         endpoint: &str,
          35  +
//!         cfg: &ConfigBag,
          36  +
//!     ) -> Result<aws_smithy_runtime_api::http::Request, SerdeError> {
          37  +
//!         todo!()
          38  +
//!     }
          39  +
//!
          40  +
//!     fn deserialize_response<'a>(
          41  +
//!         &self,
          42  +
//!         response: &'a aws_smithy_runtime_api::http::Response,
          43  +
//!         output_schema: &Schema,
          44  +
//!         cfg: &ConfigBag,
          45  +
//!     ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
          46  +
//!         todo!()
          47  +
//!     }
          48  +
//! }
          49  +
//! ```
          50  +
          51  +
use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer};
          52  +
use crate::{Schema, ShapeId};
          53  +
use aws_smithy_types::config_bag::ConfigBag;
          54  +
          55  +
// Implementation note: We use concrete aws_smithy_runtime_api::http::{Request, Response} types here
          56  +
// rather than associated types. While the SEP allows for transport-agnostic protocols (e.g., MQTT),
          57  +
// the current SDK uses HTTP throughout. If transport abstraction is needed in the future,
          58  +
// Request/Response could become associated types while maintaining object safety of this trait.
          59  +
          60  +
/// An object-safe client protocol for serializing requests and deserializing responses.
          61  +
///
          62  +
/// Each Smithy protocol (e.g., `aws.protocols#restJson1`, `smithy.protocols#rpcv2Cbor`)
          63  +
/// is represented by an implementation of this trait. Protocols combine one or more
          64  +
/// codecs and serializers to produce protocol-specific request messages and parse
          65  +
/// response messages.
          66  +
///
          67  +
/// # Lifecycle
          68  +
///
          69  +
/// `ClientProtocol` instances are immutable and thread-safe. They are typically created
          70  +
/// once and shared across all requests for a client. Serializers and deserializers are
          71  +
/// created per-request internally.
          72  +
pub trait ClientProtocol: Send + Sync + std::fmt::Debug {
          73  +
    /// Returns the Smithy shape ID of this protocol.
          74  +
    ///
          75  +
    /// This enables runtime protocol selection and differentiation. For example,
          76  +
    /// `aws.protocols#restJson1` or `smithy.protocols#rpcv2Cbor`.
          77  +
    fn protocol_id(&self) -> &ShapeId;
          78  +
          79  +
    /// Serializes an operation input into an HTTP request.
          80  +
    ///
          81  +
    /// # Arguments
          82  +
    ///
          83  +
    /// * `input` - The operation input to serialize.
          84  +
    /// * `input_schema` - Schema describing the operation's input shape.
          85  +
    /// * `endpoint` - The target endpoint URI as a string.
          86  +
    /// * `cfg` - The config bag containing request-scoped configuration
          87  +
    ///   (e.g., service name, operation name for RPC protocols).
          88  +
    fn serialize_request(
          89  +
        &self,
          90  +
        input: &dyn SerializableStruct,
          91  +
        input_schema: &Schema,
          92  +
        endpoint: &str,
          93  +
        cfg: &ConfigBag,
          94  +
    ) -> Result<aws_smithy_runtime_api::http::Request, SerdeError>;
          95  +
          96  +
    /// Deserializes an HTTP response, returning a boxed [`ShapeDeserializer`]
          97  +
    /// for the response body.
          98  +
    ///
          99  +
    /// The returned deserializer reads only body members. For outputs with
         100  +
    /// HTTP-bound members (headers, status code), generated code reads those
         101  +
    /// directly from the response before using this deserializer for body members.
         102  +
    ///
         103  +
    /// # Arguments
         104  +
    ///
         105  +
    /// * `response` - The HTTP response to deserialize.
         106  +
    /// * `output_schema` - Schema describing the operation's output shape.
         107  +
    /// * `cfg` - The config bag containing request-scoped configuration.
         108  +
    fn deserialize_response<'a>(
         109  +
        &self,
         110  +
        response: &'a aws_smithy_runtime_api::http::Response,
         111  +
        output_schema: &Schema,
         112  +
        cfg: &ConfigBag,
         113  +
    ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError>;
         114  +
         115  +
    /// Updates a previously serialized request with a new endpoint.
         116  +
    ///
         117  +
    /// Required by SEP requirement 7: "ClientProtocol MUST be able to update a
         118  +
    /// previously serialized request with a new endpoint." The orchestrator calls
         119  +
    /// this after endpoint resolution, which happens after `serialize_request`.
         120  +
    ///
         121  +
    /// The default implementation applies the endpoint URL (with prefix if present),
         122  +
    /// sets the request URI, and copies any endpoint headers onto the request.
         123  +
    /// This replicates the existing `apply_endpoint` logic from the orchestrator.
         124  +
    /// Custom implementations should rarely need to override this.
         125  +
    fn update_endpoint(
         126  +
        &self,
         127  +
        request: &mut aws_smithy_runtime_api::http::Request,
         128  +
        endpoint: &aws_smithy_types::endpoint::Endpoint,
         129  +
        cfg: &ConfigBag,
         130  +
    ) -> Result<(), SerdeError> {
         131  +
        use std::borrow::Cow;
         132  +
         133  +
        let endpoint_prefix =
         134  +
            cfg.load::<aws_smithy_runtime_api::client::endpoint::EndpointPrefix>();
         135  +
        let endpoint_url = match endpoint_prefix {
         136  +
            None => Cow::Borrowed(endpoint.url()),
         137  +
            Some(prefix) => {
         138  +
                let parsed: http::Uri = endpoint
         139  +
                    .url()
         140  +
                    .parse()
         141  +
                    .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
         142  +
                let scheme = parsed.scheme_str().unwrap_or_default();
         143  +
                let prefix = prefix.as_str();
         144  +
                let authority = parsed.authority().map(|a| a.as_str()).unwrap_or_default();
         145  +
                let path_and_query = parsed
         146  +
                    .path_and_query()
         147  +
                    .map(|pq| pq.as_str())
         148  +
                    .unwrap_or_default();
         149  +
                Cow::Owned(format!("{scheme}://{prefix}{authority}{path_and_query}"))
         150  +
            }
         151  +
        };
         152  +
         153  +
        request.uri_mut().set_endpoint(&endpoint_url).map_err(|e| {
         154  +
            SerdeError::custom(format!("failed to apply endpoint `{endpoint_url}`: {e}"))
         155  +
        })?;
         156  +
         157  +
        for (header_name, header_values) in endpoint.headers() {
         158  +
            request.headers_mut().remove(header_name);
         159  +
            for value in header_values {
         160  +
                request
         161  +
                    .headers_mut()
         162  +
                    .append(header_name.to_owned(), value.to_owned());
         163  +
            }
         164  +
        }
         165  +
         166  +
        Ok(())
         167  +
    }
         168  +
}
         169  +
         170  +
/// A shared, type-erased client protocol stored in a [`ConfigBag`].
         171  +
///
         172  +
/// This wraps an `Arc<dyn ClientProtocol>` so it can be stored
         173  +
/// and retrieved from the config bag for runtime protocol selection.
         174  +
#[derive(Clone, Debug)]
         175  +
pub struct SharedClientProtocol {
         176  +
    inner: std::sync::Arc<dyn ClientProtocol>,
         177  +
}
         178  +
         179  +
impl SharedClientProtocol {
         180  +
    /// Creates a new shared protocol from any `ClientProtocol` implementation.
         181  +
    pub fn new(protocol: impl ClientProtocol + 'static) -> Self {
         182  +
        Self {
         183  +
            inner: std::sync::Arc::new(protocol),
         184  +
        }
         185  +
    }
         186  +
}
         187  +
         188  +
impl std::ops::Deref for SharedClientProtocol {
         189  +
    type Target = dyn ClientProtocol;
         190  +
         191  +
    fn deref(&self) -> &Self::Target {
         192  +
        &*self.inner
         193  +
    }
         194  +
}
         195  +
         196  +
impl aws_smithy_types::config_bag::Storable for SharedClientProtocol {
         197  +
    type Storer = aws_smithy_types::config_bag::StoreReplace<Self>;
         198  +
}
         199  +
         200  +
#[cfg(test)]
         201  +
mod tests {
         202  +
    use super::*;
         203  +
    use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer};
         204  +
    use crate::{Schema, ShapeId};
         205  +
    use aws_smithy_runtime_api::http::{Request, Response};
         206  +
    use aws_smithy_types::body::SdkBody;
         207  +
    use aws_smithy_types::config_bag::{ConfigBag, Layer};
         208  +
    use aws_smithy_types::endpoint::Endpoint;
         209  +
         210  +
    /// Minimal protocol impl that uses only the default `update_endpoint`.
         211  +
    #[derive(Debug)]
         212  +
    struct StubProtocol;
         213  +
         214  +
    static STUB_ID: ShapeId = ShapeId::from_static("test#StubProtocol", "test", "StubProtocol");
         215  +
         216  +
    impl ClientProtocol for StubProtocol {
         217  +
        fn protocol_id(&self) -> &ShapeId {
         218  +
            &STUB_ID
         219  +
        }
         220  +
        fn serialize_request(
         221  +
            &self,
         222  +
            _input: &dyn SerializableStruct,
         223  +
            _input_schema: &Schema,
         224  +
            _endpoint: &str,
         225  +
            _cfg: &ConfigBag,
         226  +
        ) -> Result<Request, SerdeError> {
         227  +
            unimplemented!()
         228  +
        }
         229  +
        fn deserialize_response<'a>(
         230  +
            &self,
         231  +
            _response: &'a Response,
         232  +
            _output_schema: &Schema,
         233  +
            _cfg: &ConfigBag,
         234  +
        ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
         235  +
            unimplemented!()
         236  +
        }
         237  +
    }
         238  +
         239  +
    fn request_with_uri(uri: &str) -> Request {
         240  +
        let mut req = Request::new(SdkBody::empty());
         241  +
        req.set_uri(uri).unwrap();
         242  +
        req
         243  +
    }
         244  +
         245  +
    #[test]
         246  +
    fn basic_endpoint() {
         247  +
        let proto = StubProtocol;
         248  +
        let mut req = request_with_uri("/original/path");
         249  +
        let endpoint = Endpoint::builder()
         250  +
            .url("https://service.us-east-1.amazonaws.com")
         251  +
            .build();
         252  +
        let cfg = ConfigBag::base();
         253  +
         254  +
        proto.update_endpoint(&mut req, &endpoint, &cfg).unwrap();
         255  +
        assert_eq!(
         256  +
            req.uri(),
         257  +
            "https://service.us-east-1.amazonaws.com/original/path"
         258  +
        );
         259  +
    }
         260  +
         261  +
    #[test]
         262  +
    fn endpoint_with_prefix() {
         263  +
        let proto = StubProtocol;
         264  +
        let mut req = request_with_uri("/path");
         265  +
        let endpoint = Endpoint::builder()
         266  +
            .url("https://service.us-east-1.amazonaws.com")
         267  +
            .build();
         268  +
        let mut cfg = ConfigBag::base();
         269  +
        let mut layer = Layer::new("test");
         270  +
        layer.store_put(
         271  +
            aws_smithy_runtime_api::client::endpoint::EndpointPrefix::new("myprefix.").unwrap(),
         272  +
        );
         273  +
        cfg.push_shared_layer(layer.freeze());
         274  +
         275  +
        proto.update_endpoint(&mut req, &endpoint, &cfg).unwrap();
         276  +
        assert_eq!(
         277  +
            req.uri(),
         278  +
            "https://myprefix.service.us-east-1.amazonaws.com/path"
         279  +
        );
         280  +
    }
         281  +
         282  +
    #[test]
         283  +
    fn endpoint_with_headers() {
         284  +
        let proto = StubProtocol;
         285  +
        let mut req = request_with_uri("/path");
         286  +
        let endpoint = Endpoint::builder()
         287  +
            .url("https://example.com")
         288  +
            .header("x-custom", "value1")
         289  +
            .header("x-custom", "value2")
         290  +
            .build();
         291  +
        let cfg = ConfigBag::base();
         292  +
         293  +
        proto.update_endpoint(&mut req, &endpoint, &cfg).unwrap();
         294  +
        assert_eq!(req.uri(), "https://example.com/path");
         295  +
        let values: Vec<&str> = req.headers().get_all("x-custom").collect();
         296  +
        assert_eq!(values, vec!["value1", "value2"]);
         297  +
    }
         298  +
         299  +
    #[test]
         300  +
    fn endpoint_with_path() {
         301  +
        let proto = StubProtocol;
         302  +
        let mut req = request_with_uri("/operation");
         303  +
        let endpoint = Endpoint::builder().url("https://example.com/base").build();
         304  +
        let cfg = ConfigBag::base();
         305  +
         306  +
        proto.update_endpoint(&mut req, &endpoint, &cfg).unwrap();
         307  +
        assert_eq!(req.uri(), "https://example.com/base/operation");
         308  +
    }
         309  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/serde.rs

@@ -1,1 +42,42 @@
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
//! Serialization and deserialization interfaces for the Smithy data model.
    7      7   
    8      8   
mod deserializer;
    9      9   
pub mod error;
   10     10   
mod serializer;
   11     11   
   12         -
pub use deserializer::ShapeDeserializer;
          12  +
pub use deserializer::{capped_container_size, ShapeDeserializer, MAX_CONTAINER_PREALLOC};
   13     13   
pub use error::SerdeError;
   14     14   
pub use serializer::{SerializableStruct, ShapeSerializer};
   15     15   
   16     16   
#[cfg(test)]
   17     17   
mod test {
   18     18   
    use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
   19     19   
    use crate::{prelude::*, Schema};
   20     20   
   21     21   
    // Mock serializer for testing
   22     22   
    struct MockSerializer {
@@ -144,144 +247,235 @@
  164    164   
        index: usize,
  165    165   
    }
  166    166   
  167    167   
    impl MockDeserializer {
  168    168   
        fn new(values: Vec<String>) -> Self {
  169    169   
            Self { values, index: 0 }
  170    170   
        }
  171    171   
    }
  172    172   
  173    173   
    impl ShapeDeserializer for MockDeserializer {
  174         -
        fn read_struct<T, F>(
         174  +
        fn read_struct(
  175    175   
            &mut self,
  176    176   
            _schema: &Schema,
  177         -
            state: T,
  178         -
            mut consumer: F,
  179         -
        ) -> Result<T, SerdeError>
  180         -
        where
  181         -
            F: FnMut(T, &Schema, &mut Self) -> Result<T, SerdeError>,
  182         -
        {
         177  +
            consumer: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         178  +
        ) -> Result<(), SerdeError> {
  183    179   
            // Simulate reading 2 members
  184         -
            let state = consumer(state, &STRING, self)?;
  185         -
            let state = consumer(state, &INTEGER, self)?;
  186         -
            Ok(state)
         180  +
            consumer(&STRING, self)?;
         181  +
            consumer(&INTEGER, self)?;
         182  +
            Ok(())
  187    183   
        }
  188    184   
  189         -
        fn read_list<T, F>(
         185  +
        fn read_list(
  190    186   
            &mut self,
  191    187   
            _schema: &Schema,
  192         -
            mut state: T,
  193         -
            mut consumer: F,
  194         -
        ) -> Result<T, SerdeError>
  195         -
        where
  196         -
            F: FnMut(T, &mut Self) -> Result<T, SerdeError>,
  197         -
        {
         188  +
            consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         189  +
        ) -> Result<(), SerdeError> {
  198    190   
            // Simulate reading 3 elements
  199    191   
            for _ in 0..3 {
  200         -
                state = consumer(state, self)?;
         192  +
                consumer(self)?;
  201    193   
            }
  202         -
            Ok(state)
         194  +
            Ok(())
  203    195   
        }
  204    196   
  205         -
        fn read_map<T, F>(
         197  +
        fn read_map(
  206    198   
            &mut self,
  207    199   
            _schema: &Schema,
  208         -
            mut state: T,
  209         -
            mut consumer: F,
  210         -
        ) -> Result<T, SerdeError>
  211         -
        where
  212         -
            F: FnMut(T, String, &mut Self) -> Result<T, SerdeError>,
  213         -
        {
         200  +
            consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         201  +
        ) -> Result<(), SerdeError> {
  214    202   
            // Simulate reading 2 entries
  215         -
            state = consumer(state, "key1".to_string(), self)?;
  216         -
            state = consumer(state, "key2".to_string(), self)?;
  217         -
            Ok(state)
         203  +
            consumer("key1".to_string(), self)?;
         204  +
            consumer("key2".to_string(), self)?;
         205  +
            Ok(())
  218    206   
        }
  219    207   
  220    208   
        fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
  221    209   
            Ok(true)
  222    210   
        }
  223    211   
  224    212   
        fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
  225    213   
            Ok(42)
  226    214   
        }
  227    215   
@@ -330,318 +406,394 @@
  350    338   
        assert_eq!(deser.container_size(), Some(10));
  351    339   
        assert!(!deser.is_null());
  352    340   
    }
  353    341   
  354    342   
    #[test]
  355    343   
    fn test_deserializer_struct() {
  356    344   
        let mut deser = MockDeserializer::new(vec!["value1".to_string(), "value2".to_string()]);
  357    345   
  358    346   
        let mut fields = Vec::new();
  359    347   
        deser
  360         -
            .read_struct(&STRING, &mut fields, |fields, _member, d| {
         348  +
            .read_struct(&STRING, &mut |_member, d| {
  361    349   
                fields.push(d.read_string(&STRING)?);
  362         -
                Ok(fields)
         350  +
                Ok(())
  363    351   
            })
  364    352   
            .unwrap();
  365    353   
  366    354   
        assert_eq!(fields, vec!["value1", "value2"]);
  367    355   
    }
  368    356   
  369    357   
    #[test]
  370    358   
    fn test_deserializer_list() {
  371    359   
        let mut deser =
  372    360   
            MockDeserializer::new(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
  373    361   
  374    362   
        let mut elements = Vec::new();
  375    363   
        deser
  376         -
            .read_list(&STRING, &mut elements, |elements, d| {
         364  +
            .read_list(&STRING, &mut |d| {
  377    365   
                elements.push(d.read_string(&STRING)?);
  378         -
                Ok(elements)
         366  +
                Ok(())
  379    367   
            })
  380    368   
            .unwrap();
  381    369   
  382    370   
        assert_eq!(elements, vec!["a", "b", "c"]);
  383    371   
    }
  384    372   
  385    373   
    #[test]
  386    374   
    fn test_deserializer_map() {
  387    375   
        let mut deser = MockDeserializer::new(vec!["val1".to_string(), "val2".to_string()]);
  388    376   
  389    377   
        let mut entries = Vec::new();
  390    378   
        deser
  391         -
            .read_map(&STRING, &mut entries, |entries, key, d| {
         379  +
            .read_map(&STRING, &mut |key, d| {
  392    380   
                let value = d.read_string(&STRING)?;
  393    381   
                entries.push((key, value));
  394         -
                Ok(entries)
         382  +
                Ok(())
  395    383   
            })
  396    384   
            .unwrap();
  397    385   
  398    386   
        assert_eq!(
  399    387   
            entries,
  400    388   
            vec![
  401    389   
                ("key1".to_string(), "val1".to_string()),
  402    390   
                ("key2".to_string(), "val2".to_string())
  403    391   
            ]
  404    392   
        );

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/serde/deserializer.rs

@@ -1,1 +168,230 @@
   20     20   
///
   21     21   
/// # Consumer Pattern
   22     22   
///
   23     23   
/// For aggregate types, the deserializer calls a consumer function for each element/member.
   24     24   
/// The consumer receives mutable state and updates it with each deserialized value.
   25     25   
/// This pattern:
   26     26   
/// - Avoids trait object issues with generic methods
   27     27   
/// - Enables zero-cost abstractions (closures can be inlined)
   28     28   
/// - Allows caller to control deserialization order and state management
   29     29   
/// - Matches the SEP's recommendation for compiled typed languages
          30  +
/// - Uses `&mut dyn ShapeDeserializer` so composite deserializers (e.g., HTTP
          31  +
///   binding + body) can transparently delegate without the consumer knowing
          32  +
///   the concrete deserializer type. This enables runtime protocol swapping.
   30     33   
///
   31     34   
/// # Example
   32     35   
///
   33     36   
/// ```ignore
   34     37   
/// // Deserializing a structure
   35     38   
/// let mut builder = MyStructBuilder::default();
   36     39   
/// deserializer.read_struct(
   37     40   
///     &MY_STRUCT_SCHEMA,
   38         -
///     builder,
   39         -
///     |mut builder, member, deser| {
          41  +
///     &mut |member, deser| {
   40     42   
///         match member.member_index() {
   41         -
///             0 => builder.field1 = Some(deser.read_string(member)?),
   42         -
///             1 => builder.field2 = Some(deser.read_i32(member)?),
          43  +
///             Some(0) => builder.field1 = Some(deser.read_string(member)?),
          44  +
///             Some(1) => builder.field2 = Some(deser.read_integer(member)?),
   43     45   
///             _ => {}
   44     46   
///         }
   45         -
///         Ok(builder)
          47  +
///         Ok(())
   46     48   
///     },
   47     49   
/// )?;
   48     50   
/// let my_struct = builder.build();
   49     51   
/// ```
          52  +
/// Maximum pre-allocation size for containers, used to prevent denial-of-service
          53  +
/// from untrusted payloads claiming excessively large sizes.
          54  +
pub const MAX_CONTAINER_PREALLOC: usize = 10_000;
          55  +
          56  +
/// Caps a raw container size at [`MAX_CONTAINER_PREALLOC`].
          57  +
///
          58  +
/// Implementations of [`ShapeDeserializer::container_size`] SHOULD use this
          59  +
/// when returning a size derived from untrusted input (e.g., a CBOR length header).
          60  +
pub fn capped_container_size(raw: usize) -> usize {
          61  +
    raw.min(MAX_CONTAINER_PREALLOC)
          62  +
}
          63  +
   50     64   
pub trait ShapeDeserializer {
   51     65   
    /// Reads a structure from the deserializer.
   52     66   
    ///
   53         -
    /// The structure deserialization is driven by a consumer callback that is called
   54         -
    /// for each member. The consumer receives the current state, the member schema,
   55         -
    /// and the deserializer, and returns the updated state.
   56         -
    ///
   57         -
    /// # Arguments
   58         -
    ///
   59         -
    /// * `schema` - The schema of the structure being deserialized
   60         -
    /// * `state` - Initial state (typically a builder)
   61         -
    /// * `consumer` - Callback invoked for each member with (state, member_schema, deserializer)
   62         -
    ///
   63         -
    /// # Returns
   64         -
    ///
   65         -
    /// The final state after processing all members
   66         -
    fn read_struct<T, F>(
          67  +
    /// The consumer is called for each member with the member schema and a
          68  +
    /// `&mut dyn ShapeDeserializer` to read the member value. Using `dyn`
          69  +
    /// allows composite deserializers (e.g., HTTP binding + body) to
          70  +
    /// transparently delegate without the consumer knowing the concrete type.
          71  +
    fn read_struct(
   67     72   
        &mut self,
   68     73   
        schema: &Schema,
   69         -
        state: T,
   70         -
        consumer: F,
   71         -
    ) -> Result<T, SerdeError>
   72         -
    where
   73         -
        F: FnMut(T, &Schema, &mut Self) -> Result<T, SerdeError>;
          74  +
        state: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          75  +
    ) -> Result<(), SerdeError>;
   74     76   
   75     77   
    /// Reads a list from the deserializer.
   76     78   
    ///
   77         -
    /// The list deserialization is driven by a consumer callback that is called
   78         -
    /// for each element. The consumer receives the current state and the deserializer,
   79         -
    /// and returns the updated state.
   80         -
    ///
   81         -
    /// # Arguments
   82         -
    ///
   83         -
    /// * `schema` - The schema of the list being deserialized
   84         -
    /// * `state` - Initial state (typically a Vec or collection)
   85         -
    /// * `consumer` - Callback invoked for each element with (state, deserializer)
   86         -
    ///
   87         -
    /// # Returns
   88         -
    ///
   89         -
    /// The final state after processing all elements
   90         -
    fn read_list<T, F>(&mut self, schema: &Schema, state: T, consumer: F) -> Result<T, SerdeError>
   91         -
    where
   92         -
        F: FnMut(T, &mut Self) -> Result<T, SerdeError>;
          79  +
    /// The consumer is called for each element with a `&mut dyn ShapeDeserializer`.
          80  +
    fn read_list(
          81  +
        &mut self,
          82  +
        schema: &Schema,
          83  +
        state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          84  +
    ) -> Result<(), SerdeError>;
   93     85   
   94     86   
    /// Reads a map from the deserializer.
   95     87   
    ///
   96         -
    /// The map deserialization is driven by a consumer callback that is called
   97         -
    /// for each entry. The consumer receives the current state, the key, and the
   98         -
    /// deserializer, and returns the updated state.
   99         -
    ///
  100         -
    /// # Arguments
  101         -
    ///
  102         -
    /// * `schema` - The schema of the map being deserialized
  103         -
    /// * `state` - Initial state (typically a HashMap or collection)
  104         -
    /// * `consumer` - Callback invoked for each entry with (state, key, deserializer)
  105         -
    ///
  106         -
    /// # Returns
  107         -
    ///
  108         -
    /// The final state after processing all entries
  109         -
    fn read_map<T, F>(&mut self, schema: &Schema, state: T, consumer: F) -> Result<T, SerdeError>
  110         -
    where
  111         -
        F: FnMut(T, String, &mut Self) -> Result<T, SerdeError>;
          88  +
    /// The consumer is called for each entry with the key and a `&mut dyn ShapeDeserializer`.
          89  +
    fn read_map(
          90  +
        &mut self,
          91  +
        schema: &Schema,
          92  +
        state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          93  +
    ) -> Result<(), SerdeError>;
  112     94   
  113     95   
    /// Reads a boolean value.
  114     96   
    fn read_boolean(&mut self, schema: &Schema) -> Result<bool, SerdeError>;
  115     97   
  116     98   
    /// Reads a byte (i8) value.
  117     99   
    fn read_byte(&mut self, schema: &Schema) -> Result<i8, SerdeError>;
  118    100   
  119    101   
    /// Reads a short (i16) value.
  120    102   
    fn read_short(&mut self, schema: &Schema) -> Result<i16, SerdeError>;
  121    103   
  122    104   
    /// Reads an integer (i32) value.
  123    105   
    fn read_integer(&mut self, schema: &Schema) -> Result<i32, SerdeError>;
  124    106   
  125    107   
    /// Reads a long (i64) value.
  126    108   
    fn read_long(&mut self, schema: &Schema) -> Result<i64, SerdeError>;
  127    109   
  128    110   
    /// Reads a float (f32) value.
  129    111   
    fn read_float(&mut self, schema: &Schema) -> Result<f32, SerdeError>;
  130    112   
  131    113   
    /// Reads a double (f64) value.
  132    114   
    fn read_double(&mut self, schema: &Schema) -> Result<f64, SerdeError>;
  133    115   
  134    116   
    /// Reads a big integer value.
  135    117   
    fn read_big_integer(&mut self, schema: &Schema) -> Result<BigInteger, SerdeError>;
  136    118   
  137    119   
    /// Reads a big decimal value.
  138    120   
    fn read_big_decimal(&mut self, schema: &Schema) -> Result<BigDecimal, SerdeError>;
  139    121   
  140    122   
    /// Reads a string value.
  141    123   
    fn read_string(&mut self, schema: &Schema) -> Result<String, SerdeError>;
  142    124   
  143    125   
    /// Reads a blob (byte array) value.
  144    126   
    fn read_blob(&mut self, schema: &Schema) -> Result<Blob, SerdeError>;
  145    127   
  146    128   
    /// Reads a timestamp value.
  147    129   
    fn read_timestamp(&mut self, schema: &Schema) -> Result<DateTime, SerdeError>;
  148    130   
  149    131   
    /// Reads a document value.
  150    132   
    fn read_document(&mut self, schema: &Schema) -> Result<Document, SerdeError>;
  151    133   
  152    134   
    /// Checks if the current value is null.
  153    135   
    ///
  154    136   
    /// This is used for sparse collections where null values are significant.
  155    137   
    fn is_null(&self) -> bool;
  156    138   
         139  +
    /// Consumes a null value, advancing past it.
         140  +
    ///
         141  +
    /// This should be called after `is_null()` returns true to advance the
         142  +
    /// deserializer past the null token.
         143  +
    fn read_null(&mut self) -> Result<(), SerdeError> {
         144  +
        Ok(())
         145  +
    }
         146  +
  157    147   
    /// Returns the size of the current container if known.
  158    148   
    ///
  159    149   
    /// This is an optimization hint that allows pre-allocating collections
  160    150   
    /// with the correct capacity. Returns `None` if the size is unknown or
  161    151   
    /// not applicable.
  162    152   
    ///
  163         -
    /// Implementations MUST cap the returned value at a reasonable maximum
  164         -
    /// (e.g. 10,000) to prevent denial-of-service from untrusted payloads
  165         -
    /// that claim excessively large container sizes (e.g. a CBOR header
  166         -
    /// declaring billions of elements).
         153  +
    /// Implementations SHOULD cap the returned value at a reasonable maximum
         154  +
    /// (e.g., 10,000) to prevent denial-of-service from untrusted payloads
         155  +
    /// that claim excessively large container sizes (e.g., a CBOR header
         156  +
    /// declaring billions of elements). Use [`capped_container_size`] to apply
         157  +
    /// a standard cap.
  167    158   
    fn container_size(&self) -> Option<usize>;
         159  +
         160  +
    // --- Collection helper methods ---
         161  +
    //
         162  +
    // This is a **closed set** of helpers for the most common AWS collection
         163  +
    // patterns. No additional helpers will be added. New collection patterns
         164  +
    // should use the generic `read_list`/`read_map` with closures.
         165  +
    //
         166  +
    // These exist for two reasons:
         167  +
    // 1. Code size: each helper replaces ~6-8 lines of closure boilerplate in
         168  +
    //    generated code, yielding ~43% reduction for collection-heavy models.
         169  +
    // 2. Performance: codec implementations (e.g., `JsonDeserializer`) override
         170  +
    //    these to call concrete `read_string`/`read_integer`/etc. methods
         171  +
    //    directly, eliminating per-element vtable dispatch. This requires the
         172  +
    //    methods to be on the core trait (not an extension trait) since they
         173  +
    //    are called through `&mut dyn ShapeDeserializer` in generated code.
         174  +
         175  +
    /// Reads a list of strings.
         176  +
    fn read_string_list(&mut self, schema: &Schema) -> Result<Vec<String>, SerdeError> {
         177  +
        let mut out = Vec::new();
         178  +
        self.read_list(schema, &mut |deser| {
         179  +
            out.push(deser.read_string(schema)?);
         180  +
            Ok(())
         181  +
        })?;
         182  +
        Ok(out)
         183  +
    }
         184  +
         185  +
    /// Reads a list of blobs.
         186  +
    fn read_blob_list(
         187  +
        &mut self,
         188  +
        schema: &Schema,
         189  +
    ) -> Result<Vec<aws_smithy_types::Blob>, SerdeError> {
         190  +
        let mut out = Vec::new();
         191  +
        self.read_list(schema, &mut |deser| {
         192  +
            out.push(deser.read_blob(schema)?);
         193  +
            Ok(())
         194  +
        })?;
         195  +
        Ok(out)
         196  +
    }
         197  +
         198  +
    /// Reads a list of integers.
         199  +
    fn read_integer_list(&mut self, schema: &Schema) -> Result<Vec<i32>, SerdeError> {
         200  +
        let mut out = Vec::new();
         201  +
        self.read_list(schema, &mut |deser| {
         202  +
            out.push(deser.read_integer(schema)?);
         203  +
            Ok(())
         204  +
        })?;
         205  +
        Ok(out)
         206  +
    }
         207  +
         208  +
    /// Reads a list of longs.
         209  +
    fn read_long_list(&mut self, schema: &Schema) -> Result<Vec<i64>, SerdeError> {
         210  +
        let mut out = Vec::new();
         211  +
        self.read_list(schema, &mut |deser| {
         212  +
            out.push(deser.read_long(schema)?);
         213  +
            Ok(())
         214  +
        })?;
         215  +
        Ok(out)
         216  +
    }
         217  +
         218  +
    /// Reads a map with string values.
         219  +
    fn read_string_string_map(
         220  +
        &mut self,
         221  +
        schema: &Schema,
         222  +
    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
         223  +
        let mut out = std::collections::HashMap::new();
         224  +
        self.read_map(schema, &mut |key, deser| {
         225  +
            out.insert(key, deser.read_string(schema)?);
         226  +
            Ok(())
         227  +
        })?;
         228  +
        Ok(out)
         229  +
    }
  168    230   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/serde/serializer.rs

@@ -80,80 +138,213 @@
  100    100   
    fn write_blob(&mut self, schema: &Schema, value: &Blob) -> Result<(), SerdeError>;
  101    101   
  102    102   
    /// Writes a timestamp value.
  103    103   
    fn write_timestamp(&mut self, schema: &Schema, value: &DateTime) -> Result<(), SerdeError>;
  104    104   
  105    105   
    /// Writes a document value.
  106    106   
    fn write_document(&mut self, schema: &Schema, value: &Document) -> Result<(), SerdeError>;
  107    107   
  108    108   
    /// Writes a null value (for sparse collections).
  109    109   
    fn write_null(&mut self, schema: &Schema) -> Result<(), SerdeError>;
         110  +
         111  +
    // --- Collection helper methods ---
         112  +
    //
         113  +
    // This is a **closed set** of helpers for the most common AWS collection
         114  +
    // patterns. No additional helpers will be added. New collection patterns
         115  +
    // should use the generic `write_list`/`write_map` with closures.
         116  +
    //
         117  +
    // These exist for two reasons:
         118  +
    // 1. Code size: each helper replaces ~6-8 lines of closure boilerplate in
         119  +
    //    generated code, yielding ~43% reduction for collection-heavy models.
         120  +
    // 2. Performance: the corresponding `ShapeDeserializer` helpers are
         121  +
    //    overridden by codec implementations (e.g., `JsonDeserializer`) to
         122  +
    //    avoid per-element vtable dispatch. Keeping them on the core trait
         123  +
    //    (rather than an extension trait) is required because they are called
         124  +
    //    through `&mut dyn ShapeSerializer`/`&mut dyn ShapeDeserializer` in
         125  +
    //    generated `serialize_members`/`deserialize` methods.
         126  +
         127  +
    /// Writes a list of strings.
         128  +
    fn write_string_list(&mut self, schema: &Schema, values: &[String]) -> Result<(), SerdeError> {
         129  +
        self.write_list(schema, &|ser| {
         130  +
            for item in values {
         131  +
                ser.write_string(&crate::prelude::STRING, item)?;
         132  +
            }
         133  +
            Ok(())
         134  +
        })
         135  +
    }
         136  +
         137  +
    /// Writes a list of blobs.
         138  +
    fn write_blob_list(
         139  +
        &mut self,
         140  +
        schema: &Schema,
         141  +
        values: &[aws_smithy_types::Blob],
         142  +
    ) -> Result<(), SerdeError> {
         143  +
        self.write_list(schema, &|ser| {
         144  +
            for item in values {
         145  +
                ser.write_blob(&crate::prelude::BLOB, item)?;
         146  +
            }
         147  +
            Ok(())
         148  +
        })
         149  +
    }
         150  +
         151  +
    /// Writes a list of integers.
         152  +
    fn write_integer_list(&mut self, schema: &Schema, values: &[i32]) -> Result<(), SerdeError> {
         153  +
        self.write_list(schema, &|ser| {
         154  +
            for item in values {
         155  +
                ser.write_integer(&crate::prelude::INTEGER, *item)?;
         156  +
            }
         157  +
            Ok(())
         158  +
        })
         159  +
    }
         160  +
         161  +
    /// Writes a list of longs.
         162  +
    fn write_long_list(&mut self, schema: &Schema, values: &[i64]) -> Result<(), SerdeError> {
         163  +
        self.write_list(schema, &|ser| {
         164  +
            for item in values {
         165  +
                ser.write_long(&crate::prelude::LONG, *item)?;
         166  +
            }
         167  +
            Ok(())
         168  +
        })
         169  +
    }
         170  +
         171  +
    /// Writes a map with string keys and string values.
         172  +
    fn write_string_string_map(
         173  +
        &mut self,
         174  +
        schema: &Schema,
         175  +
        values: &std::collections::HashMap<String, String>,
         176  +
    ) -> Result<(), SerdeError> {
         177  +
        self.write_map(schema, &|ser| {
         178  +
            for (key, value) in values {
         179  +
                ser.write_string(&crate::prelude::STRING, key)?;
         180  +
                ser.write_string(&crate::prelude::STRING, value)?;
         181  +
            }
         182  +
            Ok(())
         183  +
        })
         184  +
    }
  110    185   
}
  111    186   
  112    187   
/// Trait for structures that can be serialized via a schema.
  113    188   
///
  114    189   
/// Implemented by generated structure types. Because `ShapeSerializer` is object-safe,
  115    190   
/// each struct gets one compiled `serialize_members()` that works with any serializer
  116    191   
/// through dynamic dispatch.
  117    192   
///
  118    193   
/// # Example
  119    194   
///