Loading codegen/src/aws_conv.rs +23 −4 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ use crate::gen::Codegen; use crate::ops::Operations; use crate::rust; use std::ops::Not; use heck::ToSnakeCase; use heck::ToUpperCamelCase; Loading Loading @@ -63,12 +65,29 @@ 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})),")); } else if field.type_ == "StreamingBlob" { continue; } if field.type_ == "StreamingBlob" { g.ln(f!("{s3s_field_name}: Some(try_from_aws(x.{aws_field_name})?),")); } else if field.option_type || field.default_value.is_some() { g.ln(f!("{s3s_field_name}: try_from_aws(x.{aws_field_name})?,")); } else { continue; } let needs_unwrap = 'unwrap: { if is_op_input(&ty.name, ops) && field.option_type.not() && field.is_required { break 'unwrap true; } field.option_type.not() && field.default_value.is_none() }; if needs_unwrap { g.ln(f!("{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.ln("})"); Loading codegen/src/dto.rs +54 −34 Original line number Diff line number Diff line Loading @@ -16,12 +16,13 @@ pub fn to_type_name(shape_name: &str) -> &str { pub type RustTypes = BTreeMap<String, rust::Type>; #[deny(clippy::shadow_unrelated)] pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes { let mut space: BTreeMap<String, rust::Type> = default(); let mut insert = |k: String, v: rust::Type| assert!(space.insert(k, v).is_none()); for (shape_name, shape) in &model.shapes { let name = match to_type_name(shape_name) { let rs_shape_name = match to_type_name(shape_name) { "SelectObjectContentEventStream" => o("SelectObjectContentEvent"), // rename s => s.to_owned(), }; Loading @@ -35,28 +36,28 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes "Event", // ]; if provided_types.contains(&name.as_str()) { let ty = rust::Type::provided(&name); insert(name, ty); if provided_types.contains(&rs_shape_name.as_str()) { let ty = rust::Type::provided(&rs_shape_name); insert(rs_shape_name, ty); continue; } match shape { smithy::Shape::Boolean(shape) => { let ty = rust::Type::alias(&name, "bool", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "bool", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Integer(shape) => { let ty = rust::Type::alias(&name, "i32", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "i32", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Long(shape) => { let ty = rust::Type::alias(&name, "i64", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "i64", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::String(shape) => { let ty = rust::Type::alias(&name, "String", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "String", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Timestamp(shape) => { let format = shape.traits.timestamp_format().map(|s| match s { Loading @@ -66,39 +67,39 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes _ => unimplemented!(), }); let ty = rust::Type::Timestamp(rust::Timestamp { name: name.clone(), name: rs_shape_name.clone(), format: format.map(o), doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Blob(_) => { unimplemented!(); } smithy::Shape::List(shape) => { let ty = rust::Type::List(rust::List { name: name.clone(), name: rs_shape_name.clone(), member: rust::ListMember { type_: to_type_name(&shape.member.target).to_owned(), xml_name: shape.member.traits.xml_name().map(o), }, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Map(shape) => { let ty = rust::Type::Map(rust::Map { name: name.clone(), name: rs_shape_name.clone(), key_type: to_type_name(&shape.key.target).to_owned(), value_type: to_type_name(&shape.value.target).to_owned(), doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Enum(shape) => { let mut variants = Vec::new(); for (variant_name, variant) in &shape.members { let name = match variant_name.as_str() { let rs_variant_name = match variant_name.as_str() { "CRC32C" => o("CRC32C"), _ => variant_name.to_shouty_snake_case(), }; Loading @@ -107,23 +108,23 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes assert!(value.is_ascii()); let variant = rust::StrEnumVariant { name, name: rs_variant_name, value, doc: variant.traits.doc().map(o), }; variants.push(variant); } let ty = rust::Type::StrEnum(rust::StrEnum { name: name.clone(), name: rs_shape_name.clone(), variants, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Structure(shape) => { let mut fields = Vec::new(); for (field_name, field) in &shape.members { let name = if field_name == "Type" { let rs_field_name = if field_name == "Type" { "type_".into() } else { field_name.to_snake_case() Loading @@ -132,11 +133,21 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes let field_type = to_type_name(&field.target).to_owned(); let default_value = field.traits.default_value().map(o); let is_required = field.traits.required(); let option_type = { let mut optional = field.traits.required().not() && default_value.is_none(); optional |= field_type == "StreamingBlob" && default_value.as_ref().unwrap() == ""; optional let is_op_input = rs_shape_name .strip_suffix("Request") .map(|op| ops.contains_key(op)) .unwrap_or(false); let option_type = 'optional: { if field_type == "StreamingBlob" && default_value.as_ref().unwrap() == "" { break 'optional true; } if is_op_input && is_required.not() { break 'optional true; } is_required.not() && default_value.is_none() }; let position = { Loading Loading @@ -168,7 +179,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes }; let field = rust::StructField { name, name: rs_field_name, type_: field_type, doc: field.traits.doc().map(o), Loading @@ -176,6 +187,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes option_type, default_value, is_required, position, Loading @@ -187,14 +199,14 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes fields.push(field); } let ty = rust::Type::Struct(rust::Struct { name: name.clone(), name: rs_shape_name.clone(), fields, doc: shape.traits.doc().map(ToOwned::to_owned), xml_name: shape.traits.xml_name().map(o), is_error_type: shape.traits.error().is_some(), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Union(shape) => { let mut variants = Vec::new(); Loading @@ -207,17 +219,24 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes variants.push(variant); } let ty = rust::Type::StructEnum(rust::StructEnum { name: name.clone(), name: rs_shape_name.clone(), variants, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Operation(_) => {} smithy::Shape::Service(_) => {} } } patch_types(&mut space); unify_operation_types(ops, &mut space); space } fn patch_types(space: &mut RustTypes) { // patch LifecycleExpiration { let Some(rust::Type::Struct(ty)) = space.get_mut("LifecycleExpiration") else { panic!() }; Loading Loading @@ -249,6 +268,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes camel_name: request.name.clone(), option_type: false, default_value: None, is_required: false, position: o("payload"), http_header: None, http_query: None, Loading @@ -260,7 +280,9 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes space.insert("SelectObjectContentInput".into(), rust::Type::Struct(ty)); space.insert("SelectObjectContentRequest".into(), rust::Type::Struct(request)); } } fn unify_operation_types(ops: &Operations, space: &mut RustTypes) { // unify operation input type for op in ops.values() { if op.name == "SelectObjectContent" { Loading Loading @@ -303,8 +325,6 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes }; assert!(space.insert(op.output.clone(), rust::Type::Struct(output_ty)).is_none()); } space } pub fn codegen(rust_types: &RustTypes, g: &mut Codegen) { Loading codegen/src/ops.rs +1 −0 Original line number Diff line number Diff line Loading @@ -510,6 +510,7 @@ fn codegen_op_http_de_multipart(op: &Operation, rust_types: &RustTypes, g: &mut "let vec_stream = req.s3ext.vec_stream.take().expect(\"missing vec stream\");", "", "let content_length = i64::try_from(vec_stream.exact_remaining_length()).map_err(|e|s3_error!(e, InvalidArgument, \"content-length overflow\"))?;", "let content_length = (content_length != 0).then_some(content_length);", "", "let body: Option<StreamingBlob> = Some(StreamingBlob::new(vec_stream));", "", Loading codegen/src/rust.rs +1 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ pub struct StructField { pub option_type: bool, pub default_value: Option<Value>, pub is_required: bool, pub position: String, Loading crates/s3s-aws/Cargo.toml +1 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ categories = ["web-programming", "web-programming::http-server"] [dependencies] async-trait = "0.1.68" aws-sdk-s3 = "0.25.0" aws-sdk-s3 = "0.25.1" aws-smithy-http = { version = "0.55.0", features = ["event-stream"] } aws-smithy-types = "0.55.0" aws-smithy-types-convert = { version = "0.55.0", features = ["convert-time"] } Loading Loading
codegen/src/aws_conv.rs +23 −4 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ use crate::gen::Codegen; use crate::ops::Operations; use crate::rust; use std::ops::Not; use heck::ToSnakeCase; use heck::ToUpperCamelCase; Loading Loading @@ -63,12 +65,29 @@ 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})),")); } else if field.type_ == "StreamingBlob" { continue; } if field.type_ == "StreamingBlob" { g.ln(f!("{s3s_field_name}: Some(try_from_aws(x.{aws_field_name})?),")); } else if field.option_type || field.default_value.is_some() { g.ln(f!("{s3s_field_name}: try_from_aws(x.{aws_field_name})?,")); } else { continue; } let needs_unwrap = 'unwrap: { if is_op_input(&ty.name, ops) && field.option_type.not() && field.is_required { break 'unwrap true; } field.option_type.not() && field.default_value.is_none() }; if needs_unwrap { g.ln(f!("{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.ln("})"); Loading
codegen/src/dto.rs +54 −34 Original line number Diff line number Diff line Loading @@ -16,12 +16,13 @@ pub fn to_type_name(shape_name: &str) -> &str { pub type RustTypes = BTreeMap<String, rust::Type>; #[deny(clippy::shadow_unrelated)] pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes { let mut space: BTreeMap<String, rust::Type> = default(); let mut insert = |k: String, v: rust::Type| assert!(space.insert(k, v).is_none()); for (shape_name, shape) in &model.shapes { let name = match to_type_name(shape_name) { let rs_shape_name = match to_type_name(shape_name) { "SelectObjectContentEventStream" => o("SelectObjectContentEvent"), // rename s => s.to_owned(), }; Loading @@ -35,28 +36,28 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes "Event", // ]; if provided_types.contains(&name.as_str()) { let ty = rust::Type::provided(&name); insert(name, ty); if provided_types.contains(&rs_shape_name.as_str()) { let ty = rust::Type::provided(&rs_shape_name); insert(rs_shape_name, ty); continue; } match shape { smithy::Shape::Boolean(shape) => { let ty = rust::Type::alias(&name, "bool", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "bool", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Integer(shape) => { let ty = rust::Type::alias(&name, "i32", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "i32", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Long(shape) => { let ty = rust::Type::alias(&name, "i64", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "i64", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::String(shape) => { let ty = rust::Type::alias(&name, "String", shape.traits.doc()); insert(name, ty); let ty = rust::Type::alias(&rs_shape_name, "String", shape.traits.doc()); insert(rs_shape_name, ty); } smithy::Shape::Timestamp(shape) => { let format = shape.traits.timestamp_format().map(|s| match s { Loading @@ -66,39 +67,39 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes _ => unimplemented!(), }); let ty = rust::Type::Timestamp(rust::Timestamp { name: name.clone(), name: rs_shape_name.clone(), format: format.map(o), doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Blob(_) => { unimplemented!(); } smithy::Shape::List(shape) => { let ty = rust::Type::List(rust::List { name: name.clone(), name: rs_shape_name.clone(), member: rust::ListMember { type_: to_type_name(&shape.member.target).to_owned(), xml_name: shape.member.traits.xml_name().map(o), }, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Map(shape) => { let ty = rust::Type::Map(rust::Map { name: name.clone(), name: rs_shape_name.clone(), key_type: to_type_name(&shape.key.target).to_owned(), value_type: to_type_name(&shape.value.target).to_owned(), doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Enum(shape) => { let mut variants = Vec::new(); for (variant_name, variant) in &shape.members { let name = match variant_name.as_str() { let rs_variant_name = match variant_name.as_str() { "CRC32C" => o("CRC32C"), _ => variant_name.to_shouty_snake_case(), }; Loading @@ -107,23 +108,23 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes assert!(value.is_ascii()); let variant = rust::StrEnumVariant { name, name: rs_variant_name, value, doc: variant.traits.doc().map(o), }; variants.push(variant); } let ty = rust::Type::StrEnum(rust::StrEnum { name: name.clone(), name: rs_shape_name.clone(), variants, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Structure(shape) => { let mut fields = Vec::new(); for (field_name, field) in &shape.members { let name = if field_name == "Type" { let rs_field_name = if field_name == "Type" { "type_".into() } else { field_name.to_snake_case() Loading @@ -132,11 +133,21 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes let field_type = to_type_name(&field.target).to_owned(); let default_value = field.traits.default_value().map(o); let is_required = field.traits.required(); let option_type = { let mut optional = field.traits.required().not() && default_value.is_none(); optional |= field_type == "StreamingBlob" && default_value.as_ref().unwrap() == ""; optional let is_op_input = rs_shape_name .strip_suffix("Request") .map(|op| ops.contains_key(op)) .unwrap_or(false); let option_type = 'optional: { if field_type == "StreamingBlob" && default_value.as_ref().unwrap() == "" { break 'optional true; } if is_op_input && is_required.not() { break 'optional true; } is_required.not() && default_value.is_none() }; let position = { Loading Loading @@ -168,7 +179,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes }; let field = rust::StructField { name, name: rs_field_name, type_: field_type, doc: field.traits.doc().map(o), Loading @@ -176,6 +187,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes option_type, default_value, is_required, position, Loading @@ -187,14 +199,14 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes fields.push(field); } let ty = rust::Type::Struct(rust::Struct { name: name.clone(), name: rs_shape_name.clone(), fields, doc: shape.traits.doc().map(ToOwned::to_owned), xml_name: shape.traits.xml_name().map(o), is_error_type: shape.traits.error().is_some(), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Union(shape) => { let mut variants = Vec::new(); Loading @@ -207,17 +219,24 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes variants.push(variant); } let ty = rust::Type::StructEnum(rust::StructEnum { name: name.clone(), name: rs_shape_name.clone(), variants, doc: shape.traits.doc().map(o), }); insert(name, ty); insert(rs_shape_name, ty); } smithy::Shape::Operation(_) => {} smithy::Shape::Service(_) => {} } } patch_types(&mut space); unify_operation_types(ops, &mut space); space } fn patch_types(space: &mut RustTypes) { // patch LifecycleExpiration { let Some(rust::Type::Struct(ty)) = space.get_mut("LifecycleExpiration") else { panic!() }; Loading Loading @@ -249,6 +268,7 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes camel_name: request.name.clone(), option_type: false, default_value: None, is_required: false, position: o("payload"), http_header: None, http_query: None, Loading @@ -260,7 +280,9 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes space.insert("SelectObjectContentInput".into(), rust::Type::Struct(ty)); space.insert("SelectObjectContentRequest".into(), rust::Type::Struct(request)); } } fn unify_operation_types(ops: &Operations, space: &mut RustTypes) { // unify operation input type for op in ops.values() { if op.name == "SelectObjectContent" { Loading Loading @@ -303,8 +325,6 @@ pub fn collect_rust_types(model: &smithy::Model, ops: &Operations) -> RustTypes }; assert!(space.insert(op.output.clone(), rust::Type::Struct(output_ty)).is_none()); } space } pub fn codegen(rust_types: &RustTypes, g: &mut Codegen) { Loading
codegen/src/ops.rs +1 −0 Original line number Diff line number Diff line Loading @@ -510,6 +510,7 @@ fn codegen_op_http_de_multipart(op: &Operation, rust_types: &RustTypes, g: &mut "let vec_stream = req.s3ext.vec_stream.take().expect(\"missing vec stream\");", "", "let content_length = i64::try_from(vec_stream.exact_remaining_length()).map_err(|e|s3_error!(e, InvalidArgument, \"content-length overflow\"))?;", "let content_length = (content_length != 0).then_some(content_length);", "", "let body: Option<StreamingBlob> = Some(StreamingBlob::new(vec_stream));", "", Loading
codegen/src/rust.rs +1 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ pub struct StructField { pub option_type: bool, pub default_value: Option<Value>, pub is_required: bool, pub position: String, Loading
crates/s3s-aws/Cargo.toml +1 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ categories = ["web-programming", "web-programming::http-server"] [dependencies] async-trait = "0.1.68" aws-sdk-s3 = "0.25.0" aws-sdk-s3 = "0.25.1" aws-smithy-http = { version = "0.55.0", features = ["event-stream"] } aws-smithy-types = "0.55.0" aws-smithy-types-convert = { version = "0.55.0", features = ["convert-time"] } Loading