rusoto_codegen 0.8.0

Code generation library for Rusoto.
Documentation
use std::io::Write;
use inflector::Inflector;

use botocore::{Operation, Service};
use super::{GenerateProtocol, error_type_name, IoResult, FileWriter};

pub struct JsonGenerator;

impl GenerateProtocol for JsonGenerator {
    fn generate_methods(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        for (operation_name, operation) in service.operations.iter() {

            let output_type = operation.output_shape_or("()");

            writeln!(writer,
                     "
                {documentation}
                {method_signature} -> Result<{output_type}, {error_type}> {{
                    let mut request = SignedRequest::new(\"{http_method}\", \"{signing_name}\", self.region, \"{request_uri}\");
                    {modify_endpoint_prefix}
                    request.set_content_type(\"application/x-amz-json-{json_version}\".to_owned());
                    request.add_header(\"x-amz-target\", \"{target_prefix}.{name}\");
                    {payload}
                    request.sign(&try!(self.credentials_provider.credentials()));

                    let response = try!(self.dispatcher.dispatch(&request));

                    match response.status {{
                        StatusCode::Ok => {{
                            {ok_response}
                        }}
                        _ => Err({error_type}::from_body(String::from_utf8_lossy(&response.body).as_ref())),
                    }}
                }}
                ",
                     documentation = generate_documentation(operation).unwrap_or("".to_owned()),
                     method_signature = generate_method_signature(operation),
                     payload = generate_payload(operation),
                     signing_name = service.signing_name(),
                     modify_endpoint_prefix = generate_endpoint_modification(service)
                         .unwrap_or("".to_owned()),
                     http_method = operation.http.method,
                     name = operation.name,
                     ok_response = generate_ok_response(operation, output_type),
                     request_uri = operation.http.request_uri,
                     target_prefix = service.metadata.target_prefix.as_ref().unwrap(),
                     json_version = service.metadata.json_version.as_ref().unwrap(),
                     error_type = error_type_name(operation_name),
                     output_type = output_type)?;
        }
        Ok(())
    }

    fn generate_prelude(&self, writer: &mut FileWriter, _service: &Service) -> IoResult {
        writeln!(writer,
                 "use serde_json;
        use signature::SignedRequest;
        use serde_json::Value as SerdeJsonValue;
        use serde_json::from_str;")
    }

    fn generate_struct_attributes(&self,
                                  serialized: bool,
                                  deserialized: bool)
                                  -> String {
        let mut derived = vec!["Default", "Debug", "Clone"];

        if serialized {
            derived.push("Serialize");
        }

        if deserialized {
            derived.push("Deserialize")
        }

        format!("#[derive({})]", derived.join(","))
    }

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

fn generate_endpoint_modification(service: &Service) -> Option<String> {
    if service.signing_name() == service.metadata.endpoint_prefix {
        None
    } else {
        Some(format!("request.set_endpoint_prefix(\"{}\".to_string());",
                     service.metadata.endpoint_prefix))
    }
}

fn generate_method_signature(operation: &Operation) -> String {
    if operation.input.is_some() {
        format!("pub fn {method_name}(&self, input: &{input_type}) ",
                input_type = operation.input_shape(),
                method_name = operation.name.to_snake_case())
    } else {
        format!("pub fn {method_name}(&self) ",
                method_name = operation.name.to_snake_case())
    }
}

fn generate_payload(operation: &Operation) -> String {
    if operation.input.is_some() {
        "let encoded = serde_json::to_string(input).unwrap();
         request.set_payload(Some(encoded.into_bytes()));
         "
            .to_string()
    } else {
        "".to_string()
    }
}

fn generate_documentation(operation: &Operation) -> Option<String> {
    operation.documentation.as_ref().map(|docs| {
        format!("#[doc=\"{}\"]",
                docs.replace("\\", "\\\\").replace("\"", "\\\""))
    })
}

fn generate_ok_response(operation: &Operation, output_type: &str) -> String {
    if operation.output.is_some() {
        format!("Ok(serde_json::from_str::<{}>(String::from_utf8_lossy(&response.body).as_ref()).unwrap())",
                output_type)
    } else {
        "Ok(())".to_owned()
    }
}