Unverified Commit 5b051f35 authored by Nugine's avatar Nugine
Browse files

codegen: refactor

parent 9a7446b7
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -10,3 +10,7 @@ regex = "1.8.1"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = { version = "1.0.96", features = ["preserve_order"] }
serde_urlencoded = "0.7.1"

[dependencies.codegen-tools]
git = "https://github.com/Nugine/codegen-tools"
rev = "284bd050b9fb763509c3faf7af23b928dea1aea3"
+50 −48
Original line number Diff line number Diff line
use crate::dto::RustTypes;
use crate::f;
use crate::gen::Codegen;
use crate::ops::Operations;
use crate::rust;

use codegen_tools::g;
use codegen_tools::glines;

use std::format as f;
use std::ops::Not;

use heck::ToSnakeCase;
use heck::ToUpperCamelCase;

pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
    g.lines([
pub fn codegen(ops: &Operations, rust_types: &RustTypes) {
    glines![
        "//! Auto generated by `codegen/src/aws_conv.rs`", //
        "",
        "use super::*;",
        "",
    ]);
    ];

    for (name, rust_type) in rust_types {
        match name.as_str() {
@@ -39,22 +41,22 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
        let s3s_path = f!("s3s::dto::{name}");
        let aws_path = aws_ty_path(name, ops, rust_types);

        g.ln(f!("impl AwsConversion for {s3s_path} {{"));
        g.ln(f!("    type Target = {aws_path};"));
        g.ln("type Error = S3Error;");
        g.lf();
        g!("impl AwsConversion for {s3s_path} {{");
        g!("    type Target = {aws_path};");
        g!("type Error = S3Error;");
        g!();

        if contains_deprecated_field(name) {
            g.ln("#[allow(deprecated)]");
            g!("#[allow(deprecated)]");
        }
        g.ln("fn try_from_aws(x: Self::Target) -> S3Result<Self> {");
        g!("fn try_from_aws(x: Self::Target) -> S3Result<Self> {{");
        match rust_type {
            rust::Type::Struct(ty) => {
                if ty.fields.is_empty() {
                    g.ln("let _ = x;");
                    g!("let _ = x;");
                }

                g.ln("Ok(Self {");
                g!("Ok(Self {{");
                for field in &ty.fields {
                    let s3s_field_name = field.name.as_str();
                    let aws_field_name = match s3s_field_name {
@@ -64,12 +66,12 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
                    };

                    if field.type_ == "SelectObjectContentEventStream" {
                        g.ln(f!("{s3s_field_name}: Some(crate::event_stream::from_aws(x.{aws_field_name})),"));
                        g!("{s3s_field_name}: Some(crate::event_stream::from_aws(x.{aws_field_name})),");
                        continue;
                    }

                    if field.type_ == "StreamingBlob" {
                        g.ln(f!("{s3s_field_name}: Some(try_from_aws(x.{aws_field_name})?),"));
                        g!("{s3s_field_name}: Some(try_from_aws(x.{aws_field_name})?),");
                        continue;
                    }

@@ -81,60 +83,60 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
                    };

                    if needs_unwrap {
                        g.ln(f!("{s3s_field_name}: unwrap_from_aws(x.{aws_field_name}, \"{s3s_field_name}\")?,"));
                        g!("{s3s_field_name}: unwrap_from_aws(x.{aws_field_name}, \"{s3s_field_name}\")?,");
                        continue;
                    }

                    // other cases
                    {
                        g.ln(f!("{s3s_field_name}: try_from_aws(x.{aws_field_name})?,"));
                        g!("{s3s_field_name}: try_from_aws(x.{aws_field_name})?,");
                    }
                }
                g.ln("})");
                g!("}})");
            }
            rust::Type::StrEnum(ty) => {
                g.ln("Ok(match x {");
                g!("Ok(match x {{");
                for variant in &ty.variants {
                    let s3s_variant_name = variant.name.as_str();
                    let aws_variant_name = match s3s_variant_name {
                        "CRC32C" => "Crc32C".to_owned(),
                        _ => s3s_variant_name.to_upper_camel_case(),
                    };
                    g.ln(f!("{aws_path}::{aws_variant_name} => Self::from_static(Self::{s3s_variant_name}),"));
                    g!("{aws_path}::{aws_variant_name} => Self::from_static(Self::{s3s_variant_name}),");
                }
                g.ln(f!("{aws_path}::Unknown(_) => Self::from(x.as_str().to_owned()),"));
                g.ln("_ => Self::from(x.as_str().to_owned()),");
                g.ln("})");
                g!("{aws_path}::Unknown(_) => Self::from(x.as_str().to_owned()),");
                g!("_ => Self::from(x.as_str().to_owned()),");
                g!("}})");
            }
            rust::Type::StructEnum(ty) => {
                g.ln("Ok(match x {");
                g!("Ok(match x {{");
                for variant in &ty.variants {
                    g.ln(f!("{aws_path}::{0}(v) => Self::{0}(try_from_aws(v)?),", variant.name));
                    g!("{aws_path}::{0}(v) => Self::{0}(try_from_aws(v)?),", variant.name);
                }
                g.ln(f!("_ => unimplemented!(\"unknown variant of {aws_path}: {{x:?}}\"),"));
                g.ln("})");
                g!("_ => unimplemented!(\"unknown variant of {aws_path}: {{x:?}}\"),");
                g!("}})");
            }
            _ => panic!(),
        }
        g.ln("}");
        g.lf();
        g!("}}");
        g!();

        if contains_deprecated_field(name) {
            g.ln("#[allow(deprecated)]");
            g!("#[allow(deprecated)]");
        }
        g.ln("fn try_into_aws(x: Self) -> S3Result<Self::Target> {");
        g!("fn try_into_aws(x: Self) -> S3Result<Self::Target> {{");
        match rust_type {
            rust::Type::Struct(ty) if ty.name == "SelectObjectContentOutput" => {
                // TODO(blocking): SelectObjectContentOutput::try_into_aws
                g.ln("drop(x);");
                g.ln("unimplemented!(\"See https://github.com/Nugine/s3s/issues/5\")");
                g!("drop(x);");
                g!("unimplemented!(\"See https://github.com/Nugine/s3s/issues/5\")");
            }
            rust::Type::Struct(ty) => {
                if ty.fields.is_empty() {
                    g.ln("let _ = x;");
                    g.ln("let y = Self::Target::builder();");
                    g!("let _ = x;");
                    g!("let y = Self::Target::builder();");
                } else {
                    g.ln("let mut y = Self::Target::builder();");
                    g!("let mut y = Self::Target::builder();");
                }

                for field in &ty.fields {
@@ -146,35 +148,35 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
                    };

                    if field.option_type {
                        g.ln(f!("y = y.set_{aws_field_name}(try_into_aws(x.{s3s_field_name})?);"));
                        g!("y = y.set_{aws_field_name}(try_into_aws(x.{s3s_field_name})?);");
                    } else {
                        g.ln(f!("y = y.set_{aws_field_name}(Some(try_into_aws(x.{s3s_field_name})?));"));
                        g!("y = y.set_{aws_field_name}(Some(try_into_aws(x.{s3s_field_name})?));");
                    }
                }

                if is_op_input(&ty.name, ops) {
                    g.ln("y.build().map_err(S3Error::internal_error)");
                    g!("y.build().map_err(S3Error::internal_error)");
                } else {
                    g.ln("Ok(y.build())");
                    g!("Ok(y.build())");
                }
            }
            rust::Type::StrEnum(_) => {
                g.ln(f!("Ok({aws_path}::from(x.as_str()))"));
                g!("Ok({aws_path}::from(x.as_str()))");
            }
            rust::Type::StructEnum(ty) => {
                g.ln("Ok(match x {");
                g!("Ok(match x {{");
                for variant in &ty.variants {
                    g.ln(f!("Self::{0}(v) => {aws_path}::{0}(try_into_aws(v)?),", variant.name));
                    g!("Self::{0}(v) => {aws_path}::{0}(try_into_aws(v)?),", variant.name);
                }
                g.ln(f!("_ => unimplemented!(\"unknown variant of {}: {{x:?}}\"),", ty.name));
                g.ln("})");
                g!("_ => unimplemented!(\"unknown variant of {}: {{x:?}}\"),", ty.name);
                g!("}})");
            }
            _ => panic!(),
        }
        g.ln("}");
        g!("}}");

        g.ln("}");
        g.lf();
        g!("}}");
        g!();
    }
}

+34 −31
Original line number Diff line number Diff line
use crate::dto::RustTypes;
use crate::f;
use crate::gen::Codegen;
use crate::ops::Operations;
use crate::rust;

use codegen_tools::g;
use codegen_tools::glines;

use std::format as f;

use heck::ToSnakeCase;

pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
    g.lines([
pub fn codegen(ops: &Operations, rust_types: &RustTypes) {
    glines![
        "//! Auto generated by `codegen/src/aws_proxy.rs`",
        "",
        "use super::*;",
@@ -20,28 +23,26 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
        "",
        "use tracing::debug;",
        "",
    ]);
    ];

    g.ln("#[async_trait::async_trait]");
    g.ln("impl S3 for Proxy {");
    g!("#[async_trait::async_trait]");
    g!("impl S3 for Proxy {{");

    for op in ops.values() {
        let method_name = op.name.to_snake_case();
        let s3s_input = f!("s3s::dto::{}", op.input);
        let s3s_output = f!("s3s::dto::{}", op.output);

        g.ln("#[tracing::instrument(skip(self, req))]");
        g.ln(f!(
            "async fn {method_name}(&self, req: S3Request<{s3s_input}>) -> S3Result<{s3s_output}> {{"
        ));
        g!("#[tracing::instrument(skip(self, req))]");
        g!("async fn {method_name}(&self, req: S3Request<{s3s_input}>) -> S3Result<{s3s_output}> {{");

        g.ln("let input = req.input;");
        g.ln("debug!(?input);");
        g!("let input = req.input;");
        g!("debug!(?input);");

        if op.smithy_input == "Unit" {
            g.ln(f!("let result = self.0.{method_name}().send().await;"));
            g!("let result = self.0.{method_name}().send().await;");
        } else {
            g.ln(f!("let mut b = self.0.{method_name}();"));
            g!("let mut b = self.0.{method_name}();");
            let rust::Type::Struct(ty) = &rust_types[op.input.as_str()] else { panic!() };

            let flattened_fields = if ty.name == "SelectObjectContentInput" {
@@ -71,31 +72,33 @@ pub fn codegen(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
                //     assert!(field.option_type);
                //     let default_val = "aws_sdk_s3::model::ChecksumAlgorithm::Sha256";
                //     let val = f!("try_into_aws(input.{s3s_field_name})?.or(Some({default_val}))");
                //     g.ln(f!("b = b.set_{aws_field_name}({val});"));
                //     g!("b = b.set_{aws_field_name}({val});");
                //     continue;
                // }

                if field.option_type {
                    g.ln(f!("b = b.set_{aws_field_name}(try_into_aws(input.{s3s_field_name})?);"));
                    g!("b = b.set_{aws_field_name}(try_into_aws(input.{s3s_field_name})?);");
                } else {
                    g.ln(f!("b = b.set_{aws_field_name}(Some(try_into_aws(input.{s3s_field_name})?));"));
                    g!("b = b.set_{aws_field_name}(Some(try_into_aws(input.{s3s_field_name})?));");
                }
            }
            g.ln("let result = b.send().await;");
            g!("let result = b.send().await;");
        }

        g.ln("match result {");
        g.ln("Ok(output) => {");
        g.ln("    let output = try_from_aws(output)?;");
        g.ln("    debug!(?output);");
        g.ln("    Ok(output)");
        g.ln("},");
        g.ln("Err(e) => Err(wrap_sdk_error!(e)),");
        g.ln("}");

        g.ln("}");
        g.lf();
        glines![
            "match result {",
            "    Ok(output) => {",
            "        let output = try_from_aws(output)?;",
            "        debug!(?output);",
            "        Ok(output)",
            "    },",
            "    Err(e) => Err(wrap_sdk_error!(e)),",
            "}",
        ];

        g!("}}");
        g!();
    }

    g.ln("}");
    g!("}}");
}
+95 −88
Original line number Diff line number Diff line
use crate::gen::Codegen;
use crate::default;
use crate::o;
use crate::ops::Operations;
use crate::rust::codegen_doc;
use crate::{default, f, o};
use crate::{rust, smithy};

use codegen_tools::g;
use codegen_tools::glines;

use std::collections::BTreeMap;
use std::ops::Not;

@@ -327,8 +330,8 @@ fn unify_operation_types(ops: &Operations, space: &mut RustTypes) {
    }
}

pub fn codegen(rust_types: &RustTypes, g: &mut Codegen) {
    g.lines([
pub fn codegen(rust_types: &RustTypes) {
    glines![
        "//! Auto generated by `codegen/src/dto.rs`",
        "",
        "#![allow(clippy::empty_structs_with_brackets)]",
@@ -341,42 +344,42 @@ pub fn codegen(rust_types: &RustTypes, g: &mut Codegen) {
        "use std::fmt;",
        "use std::str::FromStr;",
        "",
    ]);
    ];

    for rust_type in rust_types.values() {
        match rust_type {
            rust::Type::Alias(ty) => {
                codegen_doc(ty.doc.as_deref(), g);
                g.ln(f!("pub type {} = {};", ty.name, ty.type_));
                codegen_doc(ty.doc.as_deref());
                g!("pub type {} = {};", ty.name, ty.type_);
            }
            rust::Type::Provided(_) => {}
            rust::Type::List(ty) => {
                codegen_doc(ty.doc.as_deref(), g);
                g.ln(f!("pub type {} = List<{}>;", ty.name, ty.member.type_));
                codegen_doc(ty.doc.as_deref());
                g!("pub type {} = List<{}>;", ty.name, ty.member.type_);
            }
            rust::Type::Map(ty) => {
                codegen_doc(ty.doc.as_deref(), g);
                g.ln(f!("pub type {} = Map<{}, {}>;", ty.name, ty.key_type, ty.value_type));
                codegen_doc(ty.doc.as_deref());
                g!("pub type {} = Map<{}, {}>;", ty.name, ty.key_type, ty.value_type);
            }
            rust::Type::StrEnum(ty) => {
                codegen_str_enum(ty, rust_types, g);
                codegen_str_enum(ty, rust_types);
            }
            rust::Type::Struct(ty) => {
                codegen_struct(ty, rust_types, g);
                codegen_struct(ty, rust_types);
            }
            rust::Type::StructEnum(ty) => {
                codegen_struct_enum(ty, rust_types, g);
                codegen_struct_enum(ty, rust_types);
            }
            rust::Type::Timestamp(ty) => {
                codegen_doc(ty.doc.as_deref(), g);
                g.ln(f!("pub type {} = Timestamp;", ty.name));
                codegen_doc(ty.doc.as_deref());
                g!("pub type {} = Timestamp;", ty.name);
            }
        }
        g.lf();
        g!();
    }
}

fn codegen_struct(ty: &rust::Struct, _rust_types: &RustTypes, g: &mut Codegen) {
fn codegen_struct(ty: &rust::Struct, _rust_types: &RustTypes) {
    let is_rust_default = |v: &Value| match v {
        Value::Bool(x) => !x,
        Value::Number(x) => x.as_i64() == Some(0),
@@ -393,102 +396,106 @@ fn codegen_struct(ty: &rust::Struct, _rust_types: &RustTypes, g: &mut Codegen) {
        is_option || has_default
    });

    codegen_doc(ty.doc.as_deref(), g);
    codegen_doc(ty.doc.as_deref());
    if can_derive_default {
        g.ln("#[derive(Default)]");
        g!("#[derive(Default)]");
    }
    // g.ln("#[non_exhaustive]"); // TODO: builder?
    // g!("#[non_exhaustive]"); // TODO: builder?

    g.ln(f!("pub struct {} {{", ty.name));
    g!("pub struct {} {{", ty.name);
    for field in &ty.fields {
        codegen_doc(field.doc.as_deref(), g);
        codegen_doc(field.doc.as_deref());
        if field.option_type {
            g.ln(f!("    pub {}: Option<{}>,", field.name, field.type_));
            g!("    pub {}: Option<{}>,", field.name, field.type_);
        } else {
            g.ln(f!("    pub {}: {},", field.name, field.type_));
            g!("    pub {}: {},", field.name, field.type_);
        }
    }
    g.ln("}");
    g.lf();
    g!("}}");
    g!();

    g.ln(f!("impl fmt::Debug for {} {{", ty.name));
    g.ln("fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {");
    g.ln(f!("let mut d = f.debug_struct(\"{}\");", ty.name));
    g!("impl fmt::Debug for {} {{", ty.name);
    g!("fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{");
    g!("let mut d = f.debug_struct(\"{}\");", ty.name);
    for field in &ty.fields {
        if field.option_type {
            g.ln(f!("if let Some(ref val) = self.{} {{", field.name));
            g.ln(f!("d.field(\"{}\", val);", field.name));
            g.ln("}");
            g!("if let Some(ref val) = self.{} {{", field.name);
            g!("d.field(\"{}\", val);", field.name);
            g!("}}");
        } else {
            g.ln(f!("d.field(\"{0}\", &self.{0});", field.name));
            g!("d.field(\"{0}\", &self.{0});", field.name);
        }
    }
    g.ln("d.finish_non_exhaustive()");
    g.ln("}");
    g.ln("}");
    g!("d.finish_non_exhaustive()");
    g!("}}");
    g!("}}");
}

fn codegen_str_enum(ty: &rust::StrEnum, _rust_types: &RustTypes, g: &mut Codegen) {
    codegen_doc(ty.doc.as_deref(), g);
    g.ln("#[derive(Debug, Clone, PartialEq, Eq)]");
    g.ln(f!("pub struct {}(Cow<'static, str>);", ty.name));
    g.lf();
fn codegen_str_enum(ty: &rust::StrEnum, _rust_types: &RustTypes) {
    codegen_doc(ty.doc.as_deref());
    g!("#[derive(Debug, Clone, PartialEq, Eq)]");
    g!("pub struct {}(Cow<'static, str>);", ty.name);
    g!();

    g.ln(f!("impl {} {{", ty.name));
    g!("impl {} {{", ty.name);
    {
        for variant in &ty.variants {
            codegen_doc(variant.doc.as_deref(), g);
            g.ln(f!("pub const {}: &str = \"{}\";", variant.name, variant.value));
            g.lf();
        }

        g.ln("#[must_use]");
        g.ln("pub fn as_str(&self) -> &str {");
        g.ln("&self.0");
        g.ln("}");
        g.lf();

        g.ln("#[must_use]");
        g.ln("pub fn from_static(s: &'static str) -> Self {");
        g.ln("Self(Cow::from(s))");
        g.ln("}");
        g.lf();
    }
    g.ln("}");
    g.lf();

    g.ln(f!("impl From<String> for {} {{", ty.name));
    g.ln("fn from(s: String) -> Self {");
    g.ln("Self(Cow::from(s))");
    g.ln("}");
    g.ln("}");
    g.lf();

    g.ln(f!("impl From<{}> for Cow<'static, str> {{", ty.name));
    g.ln(f!("fn from(s: {}) -> Self {{", ty.name));
    g.ln("s.0");
    g.ln("}");
    g.ln("}");
    g.lf();

    g.ln(f!("impl FromStr for {} {{", ty.name));
    g.ln("type Err = Infallible;");
    g.ln("fn from_str(s: &str) -> Result<Self, Self::Err> {");
    g.ln("Ok(Self::from(s.to_owned()))");
    g.ln("}");
    g.ln("}");
}

fn codegen_struct_enum(ty: &rust::StructEnum, _rust_types: &RustTypes, g: &mut Codegen) {
    codegen_doc(ty.doc.as_deref(), g);
    g.ln("#[derive(Debug)]");
    g.ln("#[non_exhaustive]");
    g.ln(f!("pub enum {} {{", ty.name));
            codegen_doc(variant.doc.as_deref());
            g!("pub const {}: &str = \"{}\";", variant.name, variant.value);
            g!();
        }

        glines![
            "#[must_use]", //
            "pub fn as_str(&self) -> &str {",
            "&self.0",
            "}",
            "",
        ];

        glines![
            "#[must_use]",
            "pub fn from_static(s: &'static str) -> Self {",
            "Self(Cow::from(s))",
            "}",
            "",
        ];
    }
    g!("}}");
    g!();

    g!("impl From<String> for {} {{", ty.name);
    g!("fn from(s: String) -> Self {{");
    g!("Self(Cow::from(s))");
    g!("}}");
    g!("}}");
    g!();

    g!("impl From<{}> for Cow<'static, str> {{", ty.name);
    g!("fn from(s: {}) -> Self {{", ty.name);
    g!("s.0");
    g!("}}");
    g!("}}");
    g!();

    g!("impl FromStr for {} {{", ty.name);
    g!("type Err = Infallible;");
    g!("fn from_str(s: &str) -> Result<Self, Self::Err> {{");
    g!("Ok(Self::from(s.to_owned()))");
    g!("}}");
    g!("}}");
}

fn codegen_struct_enum(ty: &rust::StructEnum, _rust_types: &RustTypes) {
    codegen_doc(ty.doc.as_deref());
    g!("#[derive(Debug)]");
    g!("#[non_exhaustive]");
    g!("pub enum {} {{", ty.name);

    for variant in &ty.variants {
        codegen_doc(variant.doc.as_deref(), g);
        g.ln(f!("    {}({}),", variant.name, variant.type_));
        codegen_doc(variant.doc.as_deref());
        g!("    {}({}),", variant.name, variant.type_);
    }

    g.ln("}");
    g!("}}");
}
+59 −56

File changed.

Preview size limit exceeded, changes collapsed.

Loading