AWS SDK

AWS SDK

rev. 32b1b3c3761061baed26023be3219639e42d7d12

Files changed:

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

@@ -0,1 +0,412 @@
           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 deserialize_response() {
         364  +
        let protocol = HttpRpcProtocol::new(
         365  +
            crate::shape_id!("test", "rpc"),
         366  +
            TestCodec,
         367  +
            "application/x-amz-json-1.0",
         368  +
        );
         369  +
        let response = Response::new(
         370  +
            200u16.try_into().unwrap(),
         371  +
            SdkBody::from(r#"{"result":42}"#),
         372  +
        );
         373  +
        let mut deser = protocol
         374  +
            .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
         375  +
            .unwrap();
         376  +
        assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"result":42}"#);
         377  +
    }
         378  +
         379  +
    #[test]
         380  +
    fn update_endpoint() {
         381  +
        let protocol = HttpRpcProtocol::new(
         382  +
            crate::shape_id!("test", "rpc"),
         383  +
            TestCodec,
         384  +
            "application/x-amz-json-1.0",
         385  +
        );
         386  +
        let mut request = protocol
         387  +
            .serialize_request(
         388  +
                &EmptyStruct,
         389  +
                &TEST_SCHEMA,
         390  +
                "https://old.example.com",
         391  +
                &ConfigBag::base(),
         392  +
            )
         393  +
            .unwrap();
         394  +
        let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
         395  +
            .url("https://new.example.com")
         396  +
            .build();
         397  +
        protocol
         398  +
            .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
         399  +
            .unwrap();
         400  +
        assert_eq!(request.uri(), "https://new.example.com/");
         401  +
    }
         402  +
         403  +
    #[test]
         404  +
    fn protocol_id() {
         405  +
        let protocol = HttpRpcProtocol::new(
         406  +
            crate::shape_id!("aws.protocols", "awsJson1_0"),
         407  +
            TestCodec,
         408  +
            "application/x-amz-json-1.0",
         409  +
        );
         410  +
        assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#awsJson1_0");
         411  +
    }
         412  +
}

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

@@ -0,1 +0,241 @@
           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 hardcode aws_smithy_runtime_api::http::{Request | Response} for the types here.
          56  +
// Some other SDKs (mostly very new ones like smithy-java and smithy-python) have separated their transport
          57  +
// layer into another abstraction, so you could plug in something like MQTT instead of HTTP. It is likely
          58  +
// too late for us to do that given how deeply the assumptions about using HTTP are baked into the SDK. But
          59  +
// if we want to keep that option open we could make Request/Response associated types and maintain the object
          60  +
// safety of this trait.
          61  +
          62  +
/// An object-safe client protocol for serializing requests and deserializing responses.
          63  +
///
          64  +
/// Each Smithy protocol (e.g., `aws.protocols#restJson1`, `smithy.protocols#rpcv2Cbor`)
          65  +
/// is represented by an implementation of this trait. Protocols combine one or more
          66  +
/// codecs and serializers to produce protocol-specific request messages and parse
          67  +
/// response messages.
          68  +
///
          69  +
/// # Lifecycle
          70  +
///
          71  +
/// `ClientProtocol` instances are immutable and thread-safe. They are typically created
          72  +
/// once and shared across all requests for a client. Serializers and deserializers are
          73  +
/// created per-request internally.
          74  +
pub trait ClientProtocol: Send + Sync + std::fmt::Debug {
          75  +
    /// Returns the Smithy shape ID of this protocol.
          76  +
    ///
          77  +
    /// This enables runtime protocol selection and differentiation. For example,
          78  +
    /// `aws.protocols#restJson1` or `smithy.protocols#rpcv2Cbor`.
          79  +
    fn protocol_id(&self) -> &ShapeId;
          80  +
          81  +
    /// Serializes an operation input into an HTTP request.
          82  +
    ///
          83  +
    /// # Arguments
          84  +
    ///
          85  +
    /// * `input` - The operation input to serialize.
          86  +
    /// * `input_schema` - Schema describing the operation's input shape.
          87  +
    /// * `endpoint` - The target endpoint URI as a string.
          88  +
    /// * `cfg` - The config bag containing request-scoped configuration
          89  +
    ///   (e.g., service name, operation name for RPC protocols).
          90  +
    fn serialize_request(
          91  +
        &self,
          92  +
        input: &dyn SerializableStruct,
          93  +
        input_schema: &Schema,
          94  +
        endpoint: &str,
          95  +
        cfg: &ConfigBag,
          96  +
    ) -> Result<aws_smithy_runtime_api::http::Request, SerdeError>;
          97  +
          98  +
    /// Deserializes an HTTP response, returning a boxed [`ShapeDeserializer`]
          99  +
    /// for the response body.
         100  +
    ///
         101  +
    /// The returned deserializer reads only body members. For outputs with
         102  +
    /// HTTP-bound members (headers, status code), generated code reads those
         103  +
    /// directly from the response before using this deserializer for body members.
         104  +
    ///
         105  +
    /// # Arguments
         106  +
    ///
         107  +
    /// * `response` - The HTTP response to deserialize.
         108  +
    /// * `output_schema` - Schema describing the operation's output shape.
         109  +
    /// * `cfg` - The config bag containing request-scoped configuration.
         110  +
    fn deserialize_response<'a>(
         111  +
        &self,
         112  +
        response: &'a aws_smithy_runtime_api::http::Response,
         113  +
        output_schema: &Schema,
         114  +
        cfg: &ConfigBag,
         115  +
    ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError>;
         116  +
         117  +
    /// Returns whether this protocol uses HTTP binding traits to route members
         118  +
    /// to headers, query strings, URI labels, etc.
         119  +
    ///
         120  +
    /// When `true`, generated code may use [`serialize_body`](Self::serialize_body)
         121  +
    /// for the payload and write HTTP-bound members directly onto the request at
         122  +
    /// compile time, avoiding per-member runtime trait checks.
         123  +
    ///
         124  +
    /// When `false` (the default), generated code calls
         125  +
    /// [`serialize_request`](Self::serialize_request) which gives the protocol
         126  +
    /// full control over where every member is placed.
         127  +
    ///
         128  +
    /// This enables correct behavior when a customer swaps protocols at runtime:
         129  +
    /// an RPC protocol that puts everything in the body will return `false`,
         130  +
    /// so generated code won't hardcode header/query writes.
         131  +
    fn supports_http_bindings(&self) -> bool {
         132  +
        false
         133  +
    }
         134  +
         135  +
    /// Serializes only the body members of an operation input into an HTTP request.
         136  +
    /// For REST protocols, `serialize_request` routes each member through
         137  +
    /// `HttpBindingSerializer` which checks HTTP binding traits at runtime to
         138  +
    /// decide whether a member goes to a header, query param, URI label, or body.
         139  +
    /// For operations with many HTTP-bound members, this per-member routing adds
         140  +
    /// measurable overhead.
         141  +
    ///
         142  +
    /// This method bypasses that routing: it serializes only body members using
         143  +
    /// the codec directly, constructs the URI (with `@http` trait pattern), and
         144  +
    /// sets the HTTP method. Generated code then writes HTTP-bound members
         145  +
    /// (headers, query params, labels) directly onto the returned request.
         146  +
    ///
         147  +
    /// The default implementation delegates to `serialize_request`, which is
         148  +
    /// correct but slower for REST protocols with many HTTP bindings.
         149  +
    fn serialize_body(
         150  +
        &self,
         151  +
        input: &dyn SerializableStruct,
         152  +
        input_schema: &Schema,
         153  +
        endpoint: &str,
         154  +
        cfg: &ConfigBag,
         155  +
    ) -> Result<aws_smithy_runtime_api::http::Request, SerdeError> {
         156  +
        self.serialize_request(input, input_schema, endpoint, cfg)
         157  +
    }
         158  +
         159  +
    /// Updates a previously serialized request with a new endpoint.
         160  +
    ///
         161  +
    /// This is required by the Smithy Reference Architecture (SRA) to support
         162  +
    /// interceptors that modify the endpoint after initial serialization.
         163  +
    ///
         164  +
    /// The default implementation applies the endpoint URL (with prefix if present),
         165  +
    /// sets the request URI, and copies any endpoint headers onto the request.
         166  +
    ///
         167  +
    /// Note: the default implementation here should be sufficient for most protocols.
         168  +
    fn update_endpoint(
         169  +
        &self,
         170  +
        request: &mut aws_smithy_runtime_api::http::Request,
         171  +
        endpoint: &aws_smithy_types::endpoint::Endpoint,
         172  +
        cfg: &ConfigBag,
         173  +
    ) -> Result<(), SerdeError> {
         174  +
        use std::borrow::Cow;
         175  +
         176  +
        let endpoint_prefix =
         177  +
            cfg.load::<aws_smithy_runtime_api::client::endpoint::EndpointPrefix>();
         178  +
        let endpoint_url = match endpoint_prefix {
         179  +
            None => Cow::Borrowed(endpoint.url()),
         180  +
            Some(prefix) => {
         181  +
                let parsed: http::Uri = endpoint
         182  +
                    .url()
         183  +
                    .parse()
         184  +
                    .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
         185  +
                let scheme = parsed.scheme_str().unwrap_or_default();
         186  +
                let prefix = prefix.as_str();
         187  +
                let authority = parsed.authority().map(|a| a.as_str()).unwrap_or_default();
         188  +
                let path_and_query = parsed
         189  +
                    .path_and_query()
         190  +
                    .map(|pq| pq.as_str())
         191  +
                    .unwrap_or_default();
         192  +
                Cow::Owned(format!("{scheme}://{prefix}{authority}{path_and_query}"))
         193  +
            }
         194  +
        };
         195  +
         196  +
        request.uri_mut().set_endpoint(&endpoint_url).map_err(|e| {
         197  +
            SerdeError::custom(format!("failed to apply endpoint `{endpoint_url}`: {e}"))
         198  +
        })?;
         199  +
         200  +
        for (header_name, header_values) in endpoint.headers() {
         201  +
            request.headers_mut().remove(header_name);
         202  +
            for value in header_values {
         203  +
                request
         204  +
                    .headers_mut()
         205  +
                    .append(header_name.to_owned(), value.to_owned());
         206  +
            }
         207  +
        }
         208  +
         209  +
        Ok(())
         210  +
    }
         211  +
}
         212  +
         213  +
/// A shared, type-erased client protocol stored in a [`ConfigBag`].
         214  +
///
         215  +
/// This wraps an `Arc<dyn ClientProtocol>` so it can be stored
         216  +
/// and retrieved from the config bag for runtime protocol selection.
         217  +
#[derive(Clone, Debug)]
         218  +
pub struct SharedClientProtocol {
         219  +
    inner: std::sync::Arc<dyn ClientProtocol>,
         220  +
}
         221  +
         222  +
impl SharedClientProtocol {
         223  +
    /// Creates a new shared protocol from any `ClientProtocol` implementation.
         224  +
    pub fn new(protocol: impl ClientProtocol + 'static) -> Self {
         225  +
        Self {
         226  +
            inner: std::sync::Arc::new(protocol),
         227  +
        }
         228  +
    }
         229  +
}
         230  +
         231  +
impl std::ops::Deref for SharedClientProtocol {
         232  +
    type Target = dyn ClientProtocol;
         233  +
         234  +
    fn deref(&self) -> &Self::Target {
         235  +
        &*self.inner
         236  +
    }
         237  +
}
         238  +
         239  +
impl aws_smithy_types::config_bag::Storable for SharedClientProtocol {
         240  +
    type Storer = aws_smithy_types::config_bag::StoreReplace<Self>;
         241  +
}

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

@@ -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,219 @@
   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   
/// ```
   50     52   
pub trait ShapeDeserializer {
   51     53   
    /// Reads a structure from the deserializer.
   52     54   
    ///
   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>(
          55  +
    /// The consumer is called for each member with the member schema and a
          56  +
    /// `&mut dyn ShapeDeserializer` to read the member value. Using `dyn`
          57  +
    /// allows composite deserializers (e.g., HTTP binding + body) to
          58  +
    /// transparently delegate without the consumer knowing the concrete type.
          59  +
    fn read_struct(
   67     60   
        &mut self,
   68     61   
        schema: &Schema,
   69         -
        state: T,
   70         -
        consumer: F,
   71         -
    ) -> Result<T, SerdeError>
   72         -
    where
   73         -
        F: FnMut(T, &Schema, &mut Self) -> Result<T, SerdeError>;
          62  +
        state: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          63  +
    ) -> Result<(), SerdeError>;
   74     64   
   75     65   
    /// Reads a list from the deserializer.
   76     66   
    ///
   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>;
          67  +
    /// The consumer is called for each element with a `&mut dyn ShapeDeserializer`.
          68  +
    fn read_list(
          69  +
        &mut self,
          70  +
        schema: &Schema,
          71  +
        state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          72  +
    ) -> Result<(), SerdeError>;
   93     73   
   94     74   
    /// Reads a map from the deserializer.
   95     75   
    ///
   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>;
          76  +
    /// The consumer is called for each entry with the key and a `&mut dyn ShapeDeserializer`.
          77  +
    fn read_map(
          78  +
        &mut self,
          79  +
        schema: &Schema,
          80  +
        state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
          81  +
    ) -> Result<(), SerdeError>;
  112     82   
  113     83   
    /// Reads a boolean value.
  114     84   
    fn read_boolean(&mut self, schema: &Schema) -> Result<bool, SerdeError>;
  115     85   
  116     86   
    /// Reads a byte (i8) value.
  117     87   
    fn read_byte(&mut self, schema: &Schema) -> Result<i8, SerdeError>;
  118     88   
  119     89   
    /// Reads a short (i16) value.
  120     90   
    fn read_short(&mut self, schema: &Schema) -> Result<i16, SerdeError>;
  121     91   
  122     92   
    /// Reads an integer (i32) value.
  123     93   
    fn read_integer(&mut self, schema: &Schema) -> Result<i32, SerdeError>;
  124     94   
  125     95   
    /// Reads a long (i64) value.
  126     96   
    fn read_long(&mut self, schema: &Schema) -> Result<i64, SerdeError>;
  127     97   
  128     98   
    /// Reads a float (f32) value.
  129     99   
    fn read_float(&mut self, schema: &Schema) -> Result<f32, SerdeError>;
  130    100   
  131    101   
    /// Reads a double (f64) value.
  132    102   
    fn read_double(&mut self, schema: &Schema) -> Result<f64, SerdeError>;
  133    103   
  134    104   
    /// Reads a big integer value.
  135    105   
    fn read_big_integer(&mut self, schema: &Schema) -> Result<BigInteger, SerdeError>;
  136    106   
  137    107   
    /// Reads a big decimal value.
  138    108   
    fn read_big_decimal(&mut self, schema: &Schema) -> Result<BigDecimal, SerdeError>;
  139    109   
  140    110   
    /// Reads a string value.
  141    111   
    fn read_string(&mut self, schema: &Schema) -> Result<String, SerdeError>;
  142    112   
  143    113   
    /// Reads a blob (byte array) value.
  144    114   
    fn read_blob(&mut self, schema: &Schema) -> Result<Blob, SerdeError>;
  145    115   
  146    116   
    /// Reads a timestamp value.
  147    117   
    fn read_timestamp(&mut self, schema: &Schema) -> Result<DateTime, SerdeError>;
  148    118   
  149    119   
    /// Reads a document value.
  150    120   
    fn read_document(&mut self, schema: &Schema) -> Result<Document, SerdeError>;
  151    121   
  152    122   
    /// Checks if the current value is null.
  153    123   
    ///
  154    124   
    /// This is used for sparse collections where null values are significant.
  155    125   
    fn is_null(&self) -> bool;
  156    126   
         127  +
    /// Consumes a null value, advancing past it.
         128  +
    ///
         129  +
    /// This should be called after `is_null()` returns true to advance the
         130  +
    /// deserializer past the null token.
         131  +
    fn read_null(&mut self) -> Result<(), SerdeError> {
         132  +
        Ok(())
         133  +
    }
         134  +
  157    135   
    /// Returns the size of the current container if known.
  158    136   
    ///
  159    137   
    /// This is an optimization hint that allows pre-allocating collections
  160    138   
    /// with the correct capacity. Returns `None` if the size is unknown or
  161    139   
    /// not applicable.
  162    140   
    ///
  163    141   
    /// Implementations MUST cap the returned value at a reasonable maximum
  164    142   
    /// (e.g. 10,000) to prevent denial-of-service from untrusted payloads
  165    143   
    /// that claim excessively large container sizes (e.g. a CBOR header
  166    144   
    /// declaring billions of elements).
  167    145   
    fn container_size(&self) -> Option<usize>;
         146  +
         147  +
    // --- Collection helper methods ---
         148  +
    //
         149  +
    // These methods are not part of the Serialization and Schema Decoupling SEP.
         150  +
    // They exist as a performance optimization for common collection patterns
         151  +
    // (e.g. `List<String>`, `Map<String, String>`) that appear frequently in
         152  +
    // AWS service models. Without these, generated code must emit an inline
         153  +
    // closure calling `read_list`/`read_map` with per-element `read_string`
         154  +
    // calls — roughly 6-8 lines of boilerplate per collection field. These
         155  +
    // helpers replace that with a single method call, reducing generated code
         156  +
    // size (e.g. -43% for DynamoDB deserialize bodies).
         157  +
    //
         158  +
    // Additionally, the default implementations call through
         159  +
    // `&mut dyn ShapeDeserializer`, which introduces vtable dispatch on every
         160  +
    // element. Codec implementations (e.g. `JsonDeserializer`) can override
         161  +
    // these to call their concrete `read_string`/`read_integer`/etc. methods
         162  +
    // directly, eliminating the per-element dynamic dispatch overhead.
         163  +
         164  +
    /// Reads a list of strings.
         165  +
    fn read_string_list(&mut self, schema: &Schema) -> Result<Vec<String>, SerdeError> {
         166  +
        let mut out = Vec::new();
         167  +
        self.read_list(schema, &mut |deser| {
         168  +
            out.push(deser.read_string(schema)?);
         169  +
            Ok(())
         170  +
        })?;
         171  +
        Ok(out)
         172  +
    }
         173  +
         174  +
    /// Reads a list of blobs.
         175  +
    fn read_blob_list(
         176  +
        &mut self,
         177  +
        schema: &Schema,
         178  +
    ) -> Result<Vec<aws_smithy_types::Blob>, SerdeError> {
         179  +
        let mut out = Vec::new();
         180  +
        self.read_list(schema, &mut |deser| {
         181  +
            out.push(deser.read_blob(schema)?);
         182  +
            Ok(())
         183  +
        })?;
         184  +
        Ok(out)
         185  +
    }
         186  +
         187  +
    /// Reads a list of integers.
         188  +
    fn read_integer_list(&mut self, schema: &Schema) -> Result<Vec<i32>, SerdeError> {
         189  +
        let mut out = Vec::new();
         190  +
        self.read_list(schema, &mut |deser| {
         191  +
            out.push(deser.read_integer(schema)?);
         192  +
            Ok(())
         193  +
        })?;
         194  +
        Ok(out)
         195  +
    }
         196  +
         197  +
    /// Reads a list of longs.
         198  +
    fn read_long_list(&mut self, schema: &Schema) -> Result<Vec<i64>, SerdeError> {
         199  +
        let mut out = Vec::new();
         200  +
        self.read_list(schema, &mut |deser| {
         201  +
            out.push(deser.read_long(schema)?);
         202  +
            Ok(())
         203  +
        })?;
         204  +
        Ok(out)
         205  +
    }
         206  +
         207  +
    /// Reads a map with string values.
         208  +
    fn read_string_string_map(
         209  +
        &mut self,
         210  +
        schema: &Schema,
         211  +
    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
         212  +
        let mut out = std::collections::HashMap::new();
         213  +
        self.read_map(schema, &mut |key, deser| {
         214  +
            out.insert(key, deser.read_string(schema)?);
         215  +
            Ok(())
         216  +
        })?;
         217  +
        Ok(out)
         218  +
    }
  168    219   
}

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

@@ -80,80 +138,212 @@
  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  +
    // These methods are not part of the Serialization and Schema Decoupling SEP.
         114  +
    // They exist as a performance optimization for common collection patterns
         115  +
    // (e.g. `List<String>`, `Map<String, String>`) that appear frequently in
         116  +
    // AWS service models. Without these, generated code must emit an inline
         117  +
    // closure calling `write_list`/`write_map` with per-element `write_string`
         118  +
    // calls — roughly 6-8 lines of boilerplate per collection field. These
         119  +
    // helpers replace that with a single method call, reducing generated code
         120  +
    // size.
         121  +
    //
         122  +
    // Additionally, the default implementations call through the trait
         123  +
    // interface per element. Codec implementations can override these to write
         124  +
    // elements directly, avoiding per-element dynamic dispatch.
         125  +
         126  +
    /// Writes a list of strings.
         127  +
    fn write_string_list(&mut self, schema: &Schema, values: &[String]) -> Result<(), SerdeError> {
         128  +
        self.write_list(schema, &|ser| {
         129  +
            for item in values {
         130  +
                ser.write_string(&crate::prelude::STRING, item)?;
         131  +
            }
         132  +
            Ok(())
         133  +
        })
         134  +
    }
         135  +
         136  +
    /// Writes a list of blobs.
         137  +
    fn write_blob_list(
         138  +
        &mut self,
         139  +
        schema: &Schema,
         140  +
        values: &[aws_smithy_types::Blob],
         141  +
    ) -> Result<(), SerdeError> {
         142  +
        self.write_list(schema, &|ser| {
         143  +
            for item in values {
         144  +
                ser.write_blob(&crate::prelude::BLOB, item)?;
         145  +
            }
         146  +
            Ok(())
         147  +
        })
         148  +
    }
         149  +
         150  +
    /// Writes a list of integers.
         151  +
    fn write_integer_list(&mut self, schema: &Schema, values: &[i32]) -> Result<(), SerdeError> {
         152  +
        self.write_list(schema, &|ser| {
         153  +
            for item in values {
         154  +
                ser.write_integer(&crate::prelude::INTEGER, *item)?;
         155  +
            }
         156  +
            Ok(())
         157  +
        })
         158  +
    }
         159  +
         160  +
    /// Writes a list of longs.
         161  +
    fn write_long_list(&mut self, schema: &Schema, values: &[i64]) -> Result<(), SerdeError> {
         162  +
        self.write_list(schema, &|ser| {
         163  +
            for item in values {
         164  +
                ser.write_long(&crate::prelude::LONG, *item)?;
         165  +
            }
         166  +
            Ok(())
         167  +
        })
         168  +
    }
         169  +
         170  +
    /// Writes a map with string keys and string values.
         171  +
    fn write_string_string_map(
         172  +
        &mut self,
         173  +
        schema: &Schema,
         174  +
        values: &std::collections::HashMap<String, String>,
         175  +
    ) -> Result<(), SerdeError> {
         176  +
        self.write_map(schema, &|ser| {
         177  +
            for (key, value) in values {
         178  +
                ser.write_string(&crate::prelude::STRING, key)?;
         179  +
                ser.write_string(&crate::prelude::STRING, value)?;
         180  +
            }
         181  +
            Ok(())
         182  +
        })
         183  +
    }
  110    184   
}
  111    185   
  112    186   
/// Trait for structures that can be serialized via a schema.
  113    187   
///
  114    188   
/// Implemented by generated structure types. Because `ShapeSerializer` is object-safe,
  115    189   
/// each struct gets one compiled `serialize_members()` that works with any serializer
  116    190   
/// through dynamic dispatch.
  117    191   
///
  118    192   
/// # Example
  119    193   
///

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

@@ -165,165 +224,272 @@
  185    185   
    HttpQueryParamsTrait,
  186    186   
    "smithy.api", "httpQueryParams"
  187    187   
);
  188    188   
  189    189   
annotation_trait!(
  190    190   
    /// The `@httpResponseCode` trait — binds a member to the HTTP status code.
  191    191   
    HttpResponseCodeTrait,
  192    192   
    "smithy.api", "httpResponseCode"
  193    193   
);
  194    194   
         195  +
/// The `@http` trait — defines the HTTP method, URI pattern, and status code for an operation.
         196  +
///
         197  +
/// This is an operation-level trait that is included on the input schema for
         198  +
/// convenience, so that the protocol serializer can construct the correct
         199  +
/// request without needing a separate operation schema.
         200  +
///
         201  +
/// The URI pattern may contain `{label}` placeholders that are substituted
         202  +
/// at serialization time with percent-encoded values from `@httpLabel` members.
         203  +
#[derive(Debug, Clone)]
         204  +
pub struct HttpTrait {
         205  +
    method: &'static str,
         206  +
    uri: &'static str,
         207  +
    code: u16,
         208  +
}
         209  +
         210  +
impl HttpTrait {
         211  +
    /// Creates a new `HttpTrait`. If `code` is `None`, defaults to `200`.
         212  +
    pub const fn new(method: &'static str, uri: &'static str, code: Option<u16>) -> Self {
         213  +
        Self {
         214  +
            method,
         215  +
            uri,
         216  +
            code: match code {
         217  +
                Some(c) => c,
         218  +
                None => 200,
         219  +
            },
         220  +
        }
         221  +
    }
         222  +
         223  +
    /// The HTTP method (e.g., `"GET"`, `"POST"`, `"PUT"`).
         224  +
    pub fn method(&self) -> &str {
         225  +
        self.method
         226  +
    }
         227  +
         228  +
    /// The URI pattern (e.g., `"/resource/{id}"`).
         229  +
    ///
         230  +
    /// May contain `{label}` placeholders that correspond to `@httpLabel` members.
         231  +
    /// The protocol serializer substitutes these with percent-encoded values
         232  +
    /// collected during member serialization.
         233  +
    pub fn uri(&self) -> &str {
         234  +
        self.uri
         235  +
    }
         236  +
         237  +
    /// The HTTP status code for a successful response. Defaults to `200`.
         238  +
    pub fn code(&self) -> u16 {
         239  +
        self.code
         240  +
    }
         241  +
}
         242  +
  195    243   
// --- Streaming traits ---
  196    244   
  197    245   
annotation_trait!(
  198    246   
    /// The `@streaming` trait — marks a blob or union as streaming.
  199    247   
    StreamingTrait,
  200    248   
    "smithy.api", "streaming"
  201    249   
);
  202    250   
  203    251   
annotation_trait!(
  204    252   
    /// The `@eventHeader` trait — binds a member to an event stream header.

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-wasm/Cargo.toml

@@ -1,1 +37,37 @@
   18     18   
http-body-util = "0.1.3"
   19     19   
wstd = "0.6.5"
   20     20   
   21     21   
[dependencies.aws-smithy-async]
   22     22   
path = "../aws-smithy-async"
   23     23   
version = "1.2.14"
   24     24   
   25     25   
[dependencies.aws-smithy-runtime-api]
   26     26   
path = "../aws-smithy-runtime-api"
   27     27   
features = ["http-1x", "client"]
   28         -
version = "1.11.6"
          28  +
version = "1.11.7"
   29     29   
   30     30   
[dependencies.aws-smithy-types]
   31     31   
path = "../aws-smithy-types"
   32     32   
features = ["http-body-1-x"]
   33     33   
version = "1.4.7"
   34     34   
   35     35   
[dependencies.sync_wrapper]
   36     36   
version = "1"
   37     37   
features = ["futures"]

tmp-codegen-diff/aws-sdk/sdk/aws-types/Cargo.toml

@@ -7,7 +77,81 @@
   27     27   
tracing = "0.1.44"
   28     28   
   29     29   
[dependencies.aws-credential-types]
   30     30   
path = "../aws-credential-types"
   31     31   
version = "1.2.14"
   32     32   
   33     33   
[dependencies.aws-smithy-async]
   34     34   
path = "../aws-smithy-async"
   35     35   
version = "1.2.14"
   36     36   
          37  +
[dependencies.aws-smithy-schema]
          38  +
path = "../aws-smithy-schema"
          39  +
version = "1.0.0"
          40  +
   37     41   
[dependencies.aws-smithy-types]
   38     42   
path = "../aws-smithy-types"
   39     43   
version = "1.4.7"
   40     44   
   41     45   
[dependencies.aws-smithy-runtime]
   42     46   
path = "../aws-smithy-runtime"
   43     47   
optional = true
   44         -
version = "1.10.3"
          48  +
version = "1.10.4"
   45     49   
   46     50   
[dependencies.aws-smithy-runtime-api]
   47     51   
path = "../aws-smithy-runtime-api"
   48     52   
features = ["client"]
   49         -
version = "1.11.6"
          53  +
version = "1.11.7"
   50     54   
   51     55   
[dependencies.hyper-rustls]
   52     56   
version = "0.24.2"
   53     57   
optional = true
   54     58   
default-features = false
   55     59   
features = ["http2", "webpki-roots"]
   56     60   
   57     61   
[dev-dependencies]
   58     62   
http = "0.2.4"
   59     63   
tempfile = "3.16.0"
   60     64   
tracing-test = "0.2.5"
   61     65   
   62     66   
[dev-dependencies.tokio]
   63     67   
version = "1"
   64     68   
features = ["rt", "macros"]
   65     69   
   66     70   
[dev-dependencies.aws-smithy-runtime-api]
   67     71   
path = "../aws-smithy-runtime-api"
   68     72   
features = ["http-02x"]
   69         -
version = "1.11.6"
          73  +
version = "1.11.7"
   70     74   
   71     75   
[dev-dependencies.aws-smithy-runtime]
   72     76   
path = "../aws-smithy-runtime"
   73     77   
features = ["client"]
   74         -
version = "1.10.3"
          78  +
version = "1.10.4"
   75     79   
   76     80   
[build-dependencies]
   77     81   
rustc_version = "0.4.0"