<NME> query.rs
<BEF> use inflector::Inflector;
use std::io::Write;

use crate::botocore::{Member, Operation, Shape, ShapeType};
use crate::util;
use crate::Service;

use super::xml_payload_parser;
use super::{
    error_type_name, generate_field_name, get_rust_type, FileWriter, GenerateProtocol, IoResult,
};

pub struct QueryGenerator;

impl GenerateProtocol for QueryGenerator {
    fn generate_method_signatures(
        &self,
        writer: &mut FileWriter,
        service: &Service<'_>,
    ) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            writeln!(
                writer,
                "
                {documentation}
                {method_signature};
                ",
                documentation = generate_documentation(operation),
                method_signature = generate_method_signature(operation_name, operation, service),
            )?
        }
        Ok(())
    }

    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service<'_>) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            writeln!(writer,
                     "
                {documentation}
                {method_signature} {{
                    let mut request = SignedRequest::new(\"{http_method}\", \"{endpoint_prefix}\", &self.region, \"{request_uri}\");
                    let params = self.new_params(\"{operation_name}\");
                    {serialize_input}
                    {set_input_params}

                    let response = self.sign_and_dispatch(request, {error_type}::from_response).await?;

                    {parse_payload}
                }}
                ",
                     documentation = generate_documentation(operation),
                     error_type = error_type_name(service, operation_name),
                     http_method = &operation.http.method,
                     endpoint_prefix = service.endpoint_prefix(),
                     parse_payload =
                         xml_payload_parser::generate_response_parser(service, operation, false, "drop(response);"),
                     method_signature = generate_method_signature(operation_name, operation, service),
                     operation_name = &operation.name,
                     request_uri = &operation.http.request_uri,
                     serialize_input = generate_method_input_serialization(operation),
                     set_input_params = generate_set_input_params(operation))?;
        }
        Ok(())
    }

    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service<'_>) -> IoResult {
        writeln!(writer,
                 "use std::str::FromStr;
            use xml::EventReader;
            use rusoto_core::param::{{Params, ServiceParams}};
            use rusoto_core::request::HttpResponse;
            use rusoto_core::signature::SignedRequest;
            use rusoto_core::proto::xml::util::{{Next, Peek, XmlParseError, XmlResponse}};
            use rusoto_core::proto::xml::util::{{self as xml_util, find_start_element, skip_tree, deserialize_elements}};
            use rusoto_core::proto::xml::error::*;
            use serde_urlencoded;
            #[cfg(feature = \"serialize_structs\")]
            use serde::Serialize;
            #[cfg(feature = \"deserialize_structs\")]
            use serde::Deserialize;
            ")?;

        writeln!(
            writer,
            "
            impl {type_name} {{
                fn new_params(&self, operation_name: &str) -> Params {{
                    let mut params = Params::new();

                    params.put(\"Action\", operation_name);
                    params.put(\"Version\", \"{api_version}\");

                    params
                }}


                async fn sign_and_dispatch<E>(
                    &self,
                    request: SignedRequest,
                    from_response: fn (BufferedHttpResponse) -> RusotoError<E>,
                ) -> Result<HttpResponse, RusotoError<E>> {{
                    let mut response = self.client.sign_and_dispatch(request).await?;
                    if !response.status.is_success() {{
                        let response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
                        return Err(from_response(response));
                    }}

                    Ok(response)
                }}
            }}
            ",
            type_name = service.client_type_name(),
            api_version = service.api_version(),
        )
    }

    fn generate_serializer(
        &self,
        name: &str,
        shape: &Shape,
        service: &Service<'_>,
    ) -> Option<String> {
        if shape.is_primitive() {
            return None;
        }

        let ty = get_rust_type(service, name, shape, false, self.timestamp_type());
        Some(format!(
            "
            /// Serialize `{name}` contents to a `SignedRequest`.
            struct {name}Serializer;
            impl {name}Serializer {{
                {serializer_signature} {{
                    {serializer_body}
                }}
            }}
            ",
            name = name,
            serializer_signature = generate_serializer_signature(&ty, shape),
            serializer_body = generate_serializer_body(service, shape)
        ))
    }

    fn generate_deserializer(
        &self,
        name: &str,
        shape: &Shape,
        service: &Service<'_>,
    ) -> Option<String> {
        let ty = get_rust_type(service, name, shape, false, self.timestamp_type());
        Some(xml_payload_parser::generate_deserializer(
            name, &ty, shape, service,
        ))
    }

    fn timestamp_type(&self) -> &'static str {
        "String"
    }
}

pub fn generate_method_input_serialization(operation: &Operation) -> String {
    if operation.input.is_some() {
        format!(
            "let mut params = params;
            {input_type}Serializer::serialize(&mut params, \"\", &input);",
            input_type = operation.input.as_ref().unwrap().shape,
        )
    } else {
        String::new()
    }
}

fn generate_set_input_params(operation: &Operation) -> String {
    if operation.http.method != "POST" {
        panic!("query protocol supports only POST method: {:?}", operation);
    }
    "request.set_payload(Some(serde_urlencoded::to_string(&params).unwrap()));
    request.set_content_type(\"application/x-www-form-urlencoded\".to_owned());"
        .to_owned()
}

fn generate_serializer_body(service: &Service<'_>, shape: &Shape) -> String {
    match shape.shape_type {
        ShapeType::List => generate_list_serializer(service, shape),
        ShapeType::Map => generate_map_serializer(service, shape),
        ShapeType::Structure => generate_struct_serializer(service, shape),
        _ => "".to_owned(),
    }
}

fn generate_serializer_signature(name: &str, shape: &Shape) -> String {
    if shape.shape_type == ShapeType::Structure && shape.members.as_ref().unwrap().is_empty() {
        format!(
            "fn serialize(_params: &mut Params, name: &str, _obj: &{})",
            name
        )
fn generate_map_serializer(service: &Service, shape: &Shape) -> String {
    let mut parts = Vec::new();

    // the key is always a string type
    parts.push(format!("for (index, (key, value)) in obj.iter().enumerate() {{
            let prefix = format!(\"{{}}.{{}}\", name, index+1);
            params.put(&format!(\"{{}}.{{}}\", prefix, \"{key_name}\"), &key);",
            key_name = key_name(service, shape),
        ));


    let lmf = list_member_format(service, is_shape_flattened);

    parts.push(format!(
        "for (index, obj) in obj.iter().enumerate() {{
                    let key = format!(\"{list_member_format}\", name, index+1);",
        list_member_format = lmf
    ));

    if primitive {
        parts.push(format!(
            "params.put(&key, {});",
            serialize_primitive_expression(member_shape.shape_type, "obj")
        ));
    } else {
        parts.push(format!(
            "{}Serializer::serialize(params, &key, obj);",
            shape.member_type()
        ));
    }

    parts.push("}".to_owned());
    parts.join("\n")
}

fn list_member_format(service: &Service<'_>, flattened: bool) -> String {
    match service.protocol() {
        "ec2" => "{}.{}".to_owned(),
        "query" => {
            if flattened {
                "{}.{}".to_owned()
            } else {
                "{}.member.{}".to_owned()
            }
        }
        _ => panic!("Unsupported protocol"),
    }
}

fn generate_map_serializer(service: &Service<'_>, shape: &Shape) -> String {
    let mut parts = Vec::new();

    let prefix_snip = if service.service_id() == Some("SNS")
        && shape.value.is_some()
        && (shape.value.as_ref().unwrap().shape == "MessageAttributeValue"
            || shape.value.as_ref().unwrap().shape == "AttributeValue")
    {
        "let prefix = format!(\"{}.entry.{}\", name, index+1);".to_string()
    } else {
        "let prefix = format!(\"{}.{}\", name, index+1);".to_string()
    };

    // the key is always a string type
    parts.push(format!(
        "for (index, (key, value)) in obj.iter().enumerate() {{
            {prefix_snip}
            params.put(&format!(\"{{}}.{{}}\", prefix, \"{key_name}\"), &key);",
        prefix_snip = prefix_snip,
        key_name = key_name(service, shape),
    ));

    let value_shape = service
        .shape_for_value(shape.value.as_ref().unwrap())
        .unwrap();
    let primitive_value = value_shape.is_primitive();

    if primitive_value {
        if service.service_id() == Some("SNS") {
            parts.push("params.put(&format!(\"{}.{}\", prefix, \"value\"), &value);".to_string());
        } else {
            parts.push("params.put(&format!(\"{}.{}\", prefix, \"Value\"), &value);".to_string());
        }
    } else {
        parts.push(format!(
            "{value_type}Serializer::serialize(
                    params,
                    &format!(\"{{}}.{{}}\", prefix, \"{value_name}\"),
                    value,
                );",
            value_type = shape.value_type(),
            value_name = value_name(service, shape)
        ));
    }

    parts.push("}".to_owned());
    parts.join("\n")
}

fn key_name(service: &Service<'_>, shape: &Shape) -> String {
    let key_name = shape
        .key
        .as_ref()
        .expect("Key undefined")
        .location_name
        .as_ref()
        .map(String::as_ref)
        .unwrap_or_else(|| "key");
    capitalize_if_ec2(service, key_name)
}

fn value_name(service: &Service<'_>, shape: &Shape) -> String {
    let value_name = shape
        .value
        .as_ref()
        .expect("Value undefined")
        .location_name
        .as_ref()
        .map(String::as_ref)
        .unwrap_or_else(|| "value");
    capitalize_if_ec2(service, value_name)
}

fn member_location(service: &Service<'_>, member: &Member, default: &str) -> String {
    // Seems a bit hacky to avoid doing this just for EC2:
    if let Some(ref shape) = service.shape_for_member(member) {
        if service.protocol() != "ec2" && !shape.is_primitive() {
            if let Some(ref shape_member) = shape.member {
                let inner_name = member_location(service, shape_member, default);
                return capitalize_if_ec2(service, &inner_name);
            }
        }
    }
    let member_location = member
        .location_name
        .clone()
        .unwrap_or_else(|| default.to_owned());
    capitalize_if_ec2(service, &member_location)
}

fn capitalize_if_ec2(service: &Service<'_>, name: &str) -> String {
    match service.protocol() {
        "ec2" => util::capitalize_first(name),
        _ => name.to_owned(),
    }
}

fn generate_struct_serializer(service: &Service<'_>, shape: &Shape) -> String {
    format!(
        "let mut prefix = name.to_string();
        if prefix != \"\" {{
            prefix.push_str(\".\");
        }}

        {struct_field_serializers}
        ",
        struct_field_serializers = generate_struct_field_serializers(service, shape),
    )
}

fn generate_struct_field_serializers(service: &Service<'_>, shape: &Shape) -> String {
    shape
        .members
        .as_ref()
        .unwrap()
        .iter()
        .map(|(member_name, member)| {
            let primitive = service.shape_for_member(member).unwrap().is_primitive();

            if shape.required(member_name) {
                if primitive {
                    required_primitive_field_serializer(service, member_name, member)
                } else {
                    required_complex_field_serializer(service, member_name, member)
                }
            } else if primitive {
                optional_primitive_field_serializer(service, member_name, member)
            } else {
                optional_complex_field_serializer(service, member_name, member)
            }
        })
        .collect::<Vec<String>>()
        .join("\n")
}

fn optional_primitive_field_serializer(
    service: &Service<'_>,
    member_name: &str,
    member: &Member,
) -> String {
    if member.deprecated() {
        return "".to_owned();
    }
    let member_shape = service.shape_for_member(member).unwrap();
    let expression = serialize_primitive_expression(member_shape.shape_type, "field_value");

    format!(
        "if let Some(ref field_value) = obj.{field_name} {{
                params.put(&format!(\"{{}}{{}}\", prefix, \"{tag_name}\"), {expression});
            }}",
        field_name = generate_field_name(member_name),
        expression = expression,
        tag_name = member_location(service, member, member_name)
    )
}

fn required_primitive_field_serializer(
    service: &Service<'_>,
    member_name: &str,
    member: &Member,
) -> String {
    let member_shape = service.shape_for_member(member).unwrap();
    let expression = serialize_primitive_expression(
        member_shape.shape_type,
        &format!("obj.{}", generate_field_name(member_name)),
    );

    format!(
        "params.put(&format!(\"{{}}{{}}\", prefix, \"{tag_name}\"), {expression});",
        expression = expression,
        tag_name = member_location(service, member, member_name)
    )
}

fn serialize_primitive_expression(shape_type: ShapeType, var_name: &str) -> String {
    match shape_type {
        ShapeType::String
        | ShapeType::Timestamp
        | ShapeType::Integer
        | ShapeType::Double
        | ShapeType::Long
        | ShapeType::Boolean => format!("&{}", var_name),
        ShapeType::Blob => format!("::std::str::from_utf8(&{}).unwrap()", var_name),
        shape_type => panic!("Unknown primitive shape type: {:?}", shape_type),
    }
}

fn required_complex_field_serializer(
    service: &Service<'_>,
    member_name: &str,
    member: &Member,
) -> String {
    let tag_snip: String;
    let tag_name = member_location(service, member, member_name);
    if service.service_id() == Some("SNS") && member.shape == "MapStringToString" {
        tag_snip = format!(
            "&format!(\"{{}}{{}}.entry\", prefix, \"{tag_name}\")",
            tag_name = tag_name
        );
    } else {
        tag_snip = format!(
            "&format!(\"{{}}{{}}\", prefix, \"{tag_name}\")",
            tag_name = tag_name
        );
    }

    format!(
        "{member_shape}Serializer::serialize(
                params,
                {tag_snip},
                &obj.{field_name},
            );",
        field_name = generate_field_name(member_name),
        member_shape = member.shape,
        tag_snip = tag_snip,
    )
}

fn optional_complex_field_serializer(
    service: &Service<'_>,
    member_name: &str,
    member: &Member,
) -> String {
    let tag_snip: String;
    let tag_name = member_location(service, member, member_name);
    if service.service_id() == Some("SNS") && member.shape == "MapStringToString" {
        tag_snip = format!(
            "&format!(\"{{}}{{}}.entry\", prefix, \"{tag_name}\")",
            tag_name = tag_name
        );
    } else {
        tag_snip = format!(
            "&format!(\"{{}}{{}}\", prefix, \"{tag_name}\")",
            tag_name = tag_name
        );
    }
    format!(
        "if let Some(ref field_value) = obj.{field_name} {{
                {member_shape_name}Serializer::serialize(
                    params,
                    {tag_snip},
                    field_value,
                );
            }}",
        field_name = generate_field_name(member_name),
        member_shape_name = member.shape,
        tag_snip = tag_snip,
    )
}

fn generate_documentation(operation: &Operation) -> String {
    match operation.documentation {
        Some(ref docs) => crate::doco::Item(docs).to_string(),
        None => "".to_owned(),
    }
}

fn generate_method_signature(
    operation_name: &str,
    operation: &Operation,
    service: &Service<'_>,
) -> String {
    if operation.input.is_some() {
        format!(
            "async fn {operation_name}(&self, input: {input_type}) -> Result<{output_type}, RusotoError<{error_type}>>",
            input_type = operation.input.as_ref().unwrap().shape,
            operation_name = operation.name.to_snake_case(),
            output_type = &operation.output_shape_or("()"),
            error_type = error_type_name(service, operation_name),
        )
    } else {
        format!(
            "async fn {operation_name}(&self) -> Result<{output_type}, RusotoError<{error_type}>>",
            operation_name = operation.name.to_snake_case(),
            output_type = &operation.output_shape_or("()"),
            error_type = error_type_name(service, operation_name),
        )
    }
}

<MSG> Fix a malformed SNS Publish API request when it has message attributes

<DFF> @@ -197,10 +197,18 @@ fn list_member_format(service: &Service, flattened: bool) -> String {
 fn generate_map_serializer(service: &Service, shape: &Shape) -> String {
     let mut parts = Vec::new();
 
+    let prefix_snip: String;
+    if service.service_id() == Some("SNS") && shape.value.is_some() && shape.value.as_ref().unwrap().shape == "MessageAttributeValue" {
+        prefix_snip = "let prefix = format!(\"{}.entry.{}\", name, index+1);".to_string();
+    } else {
+        prefix_snip = "let prefix = format!(\"{}.{}\", name, index+1);".to_string();
+    }
+
     // the key is always a string type
     parts.push(format!("for (index, (key, value)) in obj.iter().enumerate() {{
-            let prefix = format!(\"{{}}.{{}}\", name, index+1);
+            {prefix_snip}
             params.put(&format!(\"{{}}.{{}}\", prefix, \"{key_name}\"), &key);",
+            prefix_snip = prefix_snip,
             key_name = key_name(service, shape),
         ));
 

{'commit': 'diff --git a/rusoto/services/sns/src/generated.rs b/rusoto/services/sns/src/generated.rs\nindex 8308263b8..274bf0b84 100644\n--- a/rusoto/services/sns/src/generated.rs\n+++ b/rusoto/services/sns/src/generated.rs\n@@ -1638,7 +1638,7 @@ impl MessageAttributeMapSerializer {\n         obj: &::std::collections::HashMap<String, MessageAttributeValue>,\n     ) {\n         for (index, (key, value)) in obj.iter().enumerate() {\n-            let prefix = format!("{}.{}", name, index + 1);\n+            let prefix = format!("{}.entry.{}", name, index + 1);\n             params.put(&format!("{}.{}", prefix, "Name"), &key);\n             MessageAttributeValueSerializer::serialize(\n                 params,\ndiff --git a/service_crategen/src/commands/generate/codegen/query.rs b/service_crategen/src/commands/generate/codegen/query.rs\nindex 92b02a44c..58d90b8d3 100644\n--- a/service_crategen/src/commands/generate/codegen/query.rs\n+++ b/service_crategen/src/commands/generate/codegen/query.rs\n@@ -197,10 +197,18 @@ fn list_member_format(service: &Service, flattened: bool) -> String {\n fn generate_map_serializer(service: &Service, shape: &Shape) -> String {\n     let mut parts = Vec::new();\n \n+    let prefix_snip: String;\n+    if service.service_id() == Some("SNS") && shape.value.is_some() && shape.value.as_ref().unwrap().shape == "MessageAttributeValue" {\n+        prefix_snip = "let prefix = format!(\\"{}.entry.{}\\", name, index+1);".to_string();\n+    } else {\n+        prefix_snip = "let prefix = format!(\\"{}.{}\\", name, index+1);".to_string();\n+    }\n+\n     // the key is always a string type\n     parts.push(format!("for (index, (key, value)) in obj.iter().enumerate() {{\n-            let prefix = format!(\\"{{}}.{{}}\\", name, index+1);\n+            {prefix_snip}\n             params.put(&format!(\\"{{}}.{{}}\\", prefix, \\"{key_name}\\"), &key);",\n+            prefix_snip = prefix_snip,\n             key_name = key_name(service, shape),\n         ));\n \n', 'commit_subject': 'Fix a malformed SNS Publish API request when it has message attributes', 'repo_name': 'rusoto/rusoto', 'committer': {'name': 'mozamimy', 'email': '522b276a356bdf39013dfabea2cd43e141ecc9e8@mozami.me', 'time_sec': '1543723000', 'tz_offset': '540', 'date': {'seconds': '1543723000'}}, 'commit_tree': '5ca998969031ec831674a4d496a65a01f28e3295', 'commit_message': 'Fix a malformed SNS Publish API request when it has message attributes\n', 'language_name': 'Shell', 'license': 'mit', 'hunks': '@@ -197,10 +197,18 @@ fn list_member_format(service: &Service, flattened: bool) -> String {\n fn generate_map_serializer(service: &Service, shape: &Shape) -> String {\n     let mut parts = Vec::new();\n \n+    let prefix_snip: String;\n+    if service.service_id() == Some("SNS") && shape.value.is_some() && shape.value.as_ref().unwrap().shape == "MessageAttributeValue" {\n+        prefix_snip = "let prefix = format!(\\"{}.entry.{}\\", name, index+1);".to_string();\n+    } else {\n+        prefix_snip = "let prefix = format!(\\"{}.{}\\", name, index+1);".to_string();\n+    }\n+\n     // the key is always a string type\n     parts.push(format!("for (index, (key, value)) in obj.iter().enumerate() {{\n-            let prefix = format!(\\"{{}}.{{}}\\", name, index+1);\n+            {prefix_snip}\n             params.put(&format!(\\"{{}}.{{}}\\", prefix, \\"{key_name}\\"), &key);",\n+            prefix_snip = prefix_snip,\n             key_name = key_name(service, shape),\n         ));\n \n', 'addition_count': 9, 'deletion_count': 1, 'src_file': 'a/service_crategen/src/commands/generate/codegen/query.rs', 'tgt_file': 'b/service_crategen/src/commands/generate/codegen/query.rs', 'file_extension': '.rs', 'before_file': 'use inflector::Inflector;\nuse std::io::Write;\n\nuse crate::botocore::{Member, Operation, Shape, ShapeType};\nuse crate::util;\nuse crate::Service;\n\nuse super::xml_payload_parser;\nuse super::{\n    error_type_name, generate_field_name, get_rust_type, FileWriter, GenerateProtocol, IoResult,\n};\n\npub struct QueryGenerator;\n\nimpl GenerateProtocol for QueryGenerator {\n    fn generate_method_signatures(\n        &self,\n        writer: &mut FileWriter,\n        service: &Service<\'_>,\n    ) -> IoResult {\n        for (operation_name, operation) in service.operations().iter() {\n            writeln!(\n                writer,\n                "\n                {documentation}\n                {method_signature};\n                ",\n                documentation = generate_documentation(operation),\n                method_signature = generate_method_signature(operation_name, operation, service),\n            )?\n        }\n        Ok(())\n    }\n\n    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service<\'_>) -> IoResult {\n        for (operation_name, operation) in service.operations().iter() {\n            writeln!(writer,\n                     "\n                {documentation}\n                {method_signature} {{\n                    let mut request = SignedRequest::new(\\"{http_method}\\", \\"{endpoint_prefix}\\", &self.region, \\"{request_uri}\\");\n                    let params = self.new_params(\\"{operation_name}\\");\n                    {serialize_input}\n                    {set_input_params}\n\n                    let response = self.sign_and_dispatch(request, {error_type}::from_response).await?;\n\n                    {parse_payload}\n                }}\n                ",\n                     documentation = generate_documentation(operation),\n                     error_type = error_type_name(service, operation_name),\n                     http_method = &operation.http.method,\n                     endpoint_prefix = service.endpoint_prefix(),\n                     parse_payload =\n                         xml_payload_parser::generate_response_parser(service, operation, false, "drop(response);"),\n                     method_signature = generate_method_signature(operation_name, operation, service),\n                     operation_name = &operation.name,\n                     request_uri = &operation.http.request_uri,\n                     serialize_input = generate_method_input_serialization(operation),\n                     set_input_params = generate_set_input_params(operation))?;\n        }\n        Ok(())\n    }\n\n    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service<\'_>) -> IoResult {\n        writeln!(writer,\n                 "use std::str::FromStr;\n            use xml::EventReader;\n            use rusoto_core::param::{{Params, ServiceParams}};\n            use rusoto_core::request::HttpResponse;\n            use rusoto_core::signature::SignedRequest;\n            use rusoto_core::proto::xml::util::{{Next, Peek, XmlParseError, XmlResponse}};\n            use rusoto_core::proto::xml::util::{{self as xml_util, find_start_element, skip_tree, deserialize_elements}};\n            use rusoto_core::proto::xml::error::*;\n            use serde_urlencoded;\n            #[cfg(feature = \\"serialize_structs\\")]\n            use serde::Serialize;\n            #[cfg(feature = \\"deserialize_structs\\")]\n            use serde::Deserialize;\n            ")?;\n\n        writeln!(\n            writer,\n            "\n            impl {type_name} {{\n                fn new_params(&self, operation_name: &str) -> Params {{\n                    let mut params = Params::new();\n\n                    params.put(\\"Action\\", operation_name);\n                    params.put(\\"Version\\", \\"{api_version}\\");\n\n                    params\n                }}\n\n\n                async fn sign_and_dispatch<E>(\n                    &self,\n                    request: SignedRequest,\n                    from_response: fn (BufferedHttpResponse) -> RusotoError<E>,\n                ) -> Result<HttpResponse, RusotoError<E>> {{\n                    let mut response = self.client.sign_and_dispatch(request).await?;\n                    if !response.status.is_success() {{\n                        let response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;\n                        return Err(from_response(response));\n                    }}\n\n                    Ok(response)\n                }}\n            }}\n            ",\n            type_name = service.client_type_name(),\n            api_version = service.api_version(),\n        )\n    }\n\n    fn generate_serializer(\n        &self,\n        name: &str,\n        shape: &Shape,\n        service: &Service<\'_>,\n    ) -> Option<String> {\n        if shape.is_primitive() {\n            return None;\n        }\n\n        let ty = get_rust_type(service, name, shape, false, self.timestamp_type());\n        Some(format!(\n            "\n            /// Serialize `{name}` contents to a `SignedRequest`.\n            struct {name}Serializer;\n            impl {name}Serializer {{\n                {serializer_signature} {{\n                    {serializer_body}\n                }}\n            }}\n            ",\n            name = name,\n            serializer_signature = generate_serializer_signature(&ty, shape),\n            serializer_body = generate_serializer_body(service, shape)\n        ))\n    }\n\n    fn generate_deserializer(\n        &self,\n        name: &str,\n        shape: &Shape,\n        service: &Service<\'_>,\n    ) -> Option<String> {\n        let ty = get_rust_type(service, name, shape, false, self.timestamp_type());\n        Some(xml_payload_parser::generate_deserializer(\n            name, &ty, shape, service,\n        ))\n    }\n\n    fn timestamp_type(&self) -> &\'static str {\n        "String"\n    }\n}\n\npub fn generate_method_input_serialization(operation: &Operation) -> String {\n    if operation.input.is_some() {\n        format!(\n            "let mut params = params;\n            {input_type}Serializer::serialize(&mut params, \\"\\", &input);",\n            input_type = operation.input.as_ref().unwrap().shape,\n        )\n    } else {\n        String::new()\n    }\n}\n\nfn generate_set_input_params(operation: &Operation) -> String {\n    if operation.http.method != "POST" {\n        panic!("query protocol supports only POST method: {:?}", operation);\n    }\n    "request.set_payload(Some(serde_urlencoded::to_string(&params).unwrap()));\n    request.set_content_type(\\"application/x-www-form-urlencoded\\".to_owned());"\n        .to_owned()\n}\n\nfn generate_serializer_body(service: &Service<\'_>, shape: &Shape) -> String {\n    match shape.shape_type {\n        ShapeType::List => generate_list_serializer(service, shape),\n        ShapeType::Map => generate_map_serializer(service, shape),\n        ShapeType::Structure => generate_struct_serializer(service, shape),\n        _ => "".to_owned(),\n    }\n}\n\nfn generate_serializer_signature(name: &str, shape: &Shape) -> String {\n    if shape.shape_type == ShapeType::Structure && shape.members.as_ref().unwrap().is_empty() {\n        format!(\n            "fn serialize(_params: &mut Params, name: &str, _obj: &{})",\n            name\n        )\nfn generate_map_serializer(service: &Service, shape: &Shape) -> String {\n    let mut parts = Vec::new();\n\n    // the key is always a string type\n    parts.push(format!("for (index, (key, value)) in obj.iter().enumerate() {{\n            let prefix = format!(\\"{{}}.{{}}\\", name, index+1);\n            params.put(&format!(\\"{{}}.{{}}\\", prefix, \\"{key_name}\\"), &key);",\n            key_name = key_name(service, shape),\n        ));\n\n\n    let lmf = list_member_format(service, is_shape_flattened);\n\n    parts.push(format!(\n        "for (index, obj) in obj.iter().enumerate() {{\n                    let key = format!(\\"{list_member_format}\\", name, index+1);",\n        list_member_format = lmf\n    ));\n\n    if primitive {\n        parts.push(format!(\n            "params.put(&key, {});",\n            serialize_primitive_expression(member_shape.shape_type, "obj")\n        ));\n    } else {\n        parts.push(format!(\n            "{}Serializer::serialize(params, &key, obj);",\n            shape.member_type()\n        ));\n    }\n\n    parts.push("}".to_owned());\n    parts.join("\\n")\n}\n\nfn list_member_format(service: &Service<\'_>, flattened: bool) -> String {\n    match service.protocol() {\n        "ec2" => "{}.{}".to_owned(),\n        "query" => {\n            if flattened {\n                "{}.{}".to_owned()\n            } else {\n                "{}.member.{}".to_owned()\n            }\n        }\n        _ => panic!("Unsupported protocol"),\n    }\n}\n\nfn generate_map_serializer(service: &Service<\'_>, shape: &Shape) -> String {\n    let mut parts = Vec::new();\n\n    let prefix_snip = if service.service_id() == Some("SNS")\n        && shape.value.is_some()\n        && (shape.value.as_ref().unwrap().shape == "MessageAttributeValue"\n            || shape.value.as_ref().unwrap().shape == "AttributeValue")\n    {\n        "let prefix = format!(\\"{}.entry.{}\\", name, index+1);".to_string()\n    } else {\n        "let prefix = format!(\\"{}.{}\\", name, index+1);".to_string()\n    };\n\n    // the key is always a string type\n    parts.push(format!(\n        "for (index, (key, value)) in obj.iter().enumerate() {{\n            {prefix_snip}\n            params.put(&format!(\\"{{}}.{{}}\\", prefix, \\"{key_name}\\"), &key);",\n        prefix_snip = prefix_snip,\n        key_name = key_name(service, shape),\n    ));\n\n    let value_shape = service\n        .shape_for_value(shape.value.as_ref().unwrap())\n        .unwrap();\n    let primitive_value = value_shape.is_primitive();\n\n    if primitive_value {\n        if service.service_id() == Some("SNS") {\n            parts.push("params.put(&format!(\\"{}.{}\\", prefix, \\"value\\"), &value);".to_string());\n        } else {\n            parts.push("params.put(&format!(\\"{}.{}\\", prefix, \\"Value\\"), &value);".to_string());\n        }\n    } else {\n        parts.push(format!(\n            "{value_type}Serializer::serialize(\n                    params,\n                    &format!(\\"{{}}.{{}}\\", prefix, \\"{value_name}\\"),\n                    value,\n                );",\n            value_type = shape.value_type(),\n            value_name = value_name(service, shape)\n        ));\n    }\n\n    parts.push("}".to_owned());\n    parts.join("\\n")\n}\n\nfn key_name(service: &Service<\'_>, shape: &Shape) -> String {\n    let key_name = shape\n        .key\n        .as_ref()\n        .expect("Key undefined")\n        .location_name\n        .as_ref()\n        .map(String::as_ref)\n        .unwrap_or_else(|| "key");\n    capitalize_if_ec2(service, key_name)\n}\n\nfn value_name(service: &Service<\'_>, shape: &Shape) -> String {\n    let value_name = shape\n        .value\n        .as_ref()\n        .expect("Value undefined")\n        .location_name\n        .as_ref()\n        .map(String::as_ref)\n        .unwrap_or_else(|| "value");\n    capitalize_if_ec2(service, value_name)\n}\n\nfn member_location(service: &Service<\'_>, member: &Member, default: &str) -> String {\n    // Seems a bit hacky to avoid doing this just for EC2:\n    if let Some(ref shape) = service.shape_for_member(member) {\n        if service.protocol() != "ec2" && !shape.is_primitive() {\n            if let Some(ref shape_member) = shape.member {\n                let inner_name = member_location(service, shape_member, default);\n                return capitalize_if_ec2(service, &inner_name);\n            }\n        }\n    }\n    let member_location = member\n        .location_name\n        .clone()\n        .unwrap_or_else(|| default.to_owned());\n    capitalize_if_ec2(service, &member_location)\n}\n\nfn capitalize_if_ec2(service: &Service<\'_>, name: &str) -> String {\n    match service.protocol() {\n        "ec2" => util::capitalize_first(name),\n        _ => name.to_owned(),\n    }\n}\n\nfn generate_struct_serializer(service: &Service<\'_>, shape: &Shape) -> String {\n    format!(\n        "let mut prefix = name.to_string();\n        if prefix != \\"\\" {{\n            prefix.push_str(\\".\\");\n        }}\n\n        {struct_field_serializers}\n        ",\n        struct_field_serializers = generate_struct_field_serializers(service, shape),\n    )\n}\n\nfn generate_struct_field_serializers(service: &Service<\'_>, shape: &Shape) -> String {\n    shape\n        .members\n        .as_ref()\n        .unwrap()\n        .iter()\n        .map(|(member_name, member)| {\n            let primitive = service.shape_for_member(member).unwrap().is_primitive();\n\n            if shape.required(member_name) {\n                if primitive {\n                    required_primitive_field_serializer(service, member_name, member)\n                } else {\n                    required_complex_field_serializer(service, member_name, member)\n                }\n            } else if primitive {\n                optional_primitive_field_serializer(service, member_name, member)\n            } else {\n                optional_complex_field_serializer(service, member_name, member)\n            }\n        })\n        .collect::<Vec<String>>()\n        .join("\\n")\n}\n\nfn optional_primitive_field_serializer(\n    service: &Service<\'_>,\n    member_name: &str,\n    member: &Member,\n) -> String {\n    if member.deprecated() {\n        return "".to_owned();\n    }\n    let member_shape = service.shape_for_member(member).unwrap();\n    let expression = serialize_primitive_expression(member_shape.shape_type, "field_value");\n\n    format!(\n        "if let Some(ref field_value) = obj.{field_name} {{\n                params.put(&format!(\\"{{}}{{}}\\", prefix, \\"{tag_name}\\"), {expression});\n            }}",\n        field_name = generate_field_name(member_name),\n        expression = expression,\n        tag_name = member_location(service, member, member_name)\n    )\n}\n\nfn required_primitive_field_serializer(\n    service: &Service<\'_>,\n    member_name: &str,\n    member: &Member,\n) -> String {\n    let member_shape = service.shape_for_member(member).unwrap();\n    let expression = serialize_primitive_expression(\n        member_shape.shape_type,\n        &format!("obj.{}", generate_field_name(member_name)),\n    );\n\n    format!(\n        "params.put(&format!(\\"{{}}{{}}\\", prefix, \\"{tag_name}\\"), {expression});",\n        expression = expression,\n        tag_name = member_location(service, member, member_name)\n    )\n}\n\nfn serialize_primitive_expression(shape_type: ShapeType, var_name: &str) -> String {\n    match shape_type {\n        ShapeType::String\n        | ShapeType::Timestamp\n        | ShapeType::Integer\n        | ShapeType::Double\n        | ShapeType::Long\n        | ShapeType::Boolean => format!("&{}", var_name),\n        ShapeType::Blob => format!("::std::str::from_utf8(&{}).unwrap()", var_name),\n        shape_type => panic!("Unknown primitive shape type: {:?}", shape_type),\n    }\n}\n\nfn required_complex_field_serializer(\n    service: &Service<\'_>,\n    member_name: &str,\n    member: &Member,\n) -> String {\n    let tag_snip: String;\n    let tag_name = member_location(service, member, member_name);\n    if service.service_id() == Some("SNS") && member.shape == "MapStringToString" {\n        tag_snip = format!(\n            "&format!(\\"{{}}{{}}.entry\\", prefix, \\"{tag_name}\\")",\n            tag_name = tag_name\n        );\n    } else {\n        tag_snip = format!(\n            "&format!(\\"{{}}{{}}\\", prefix, \\"{tag_name}\\")",\n            tag_name = tag_name\n        );\n    }\n\n    format!(\n        "{member_shape}Serializer::serialize(\n                params,\n                {tag_snip},\n                &obj.{field_name},\n            );",\n        field_name = generate_field_name(member_name),\n        member_shape = member.shape,\n        tag_snip = tag_snip,\n    )\n}\n\nfn optional_complex_field_serializer(\n    service: &Service<\'_>,\n    member_name: &str,\n    member: &Member,\n) -> String {\n    let tag_snip: String;\n    let tag_name = member_location(service, member, member_name);\n    if service.service_id() == Some("SNS") && member.shape == "MapStringToString" {\n        tag_snip = format!(\n            "&format!(\\"{{}}{{}}.entry\\", prefix, \\"{tag_name}\\")",\n            tag_name = tag_name\n        );\n    } else {\n        tag_snip = format!(\n            "&format!(\\"{{}}{{}}\\", prefix, \\"{tag_name}\\")",\n            tag_name = tag_name\n        );\n    }\n    format!(\n        "if let Some(ref field_value) = obj.{field_name} {{\n                {member_shape_name}Serializer::serialize(\n                    params,\n                    {tag_snip},\n                    field_value,\n                );\n            }}",\n        field_name = generate_field_name(member_name),\n        member_shape_name = member.shape,\n        tag_snip = tag_snip,\n    )\n}\n\nfn generate_documentation(operation: &Operation) -> String {\n    match operation.documentation {\n        Some(ref docs) => crate::doco::Item(docs).to_string(),\n        None => "".to_owned(),\n    }\n}\n\nfn generate_method_signature(\n    operation_name: &str,\n    operation: &Operation,\n    service: &Service<\'_>,\n) -> String {\n    if operation.input.is_some() {\n        format!(\n            "async fn {operation_name}(&self, input: {input_type}) -> Result<{output_type}, RusotoError<{error_type}>>",\n            input_type = operation.input.as_ref().unwrap().shape,\n            operation_name = operation.name.to_snake_case(),\n            output_type = &operation.output_shape_or("()"),\n            error_type = error_type_name(service, operation_name),\n        )\n    } else {\n        format!(\n            "async fn {operation_name}(&self) -> Result<{output_type}, RusotoError<{error_type}>>",\n            operation_name = operation.name.to_snake_case(),\n            output_type = &operation.output_shape_or("()"),\n            error_type = error_type_name(service, operation_name),\n        )\n    }\n}\n', 'message': 'Fix a malformed SNS Publish API request when it has message attributes\n', 'author_name': 'mozamimy', 'lang': 'rs'}