Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking change: improve as_/is_ helpers #527

Merged
merged 10 commits into from
Jun 22, 2021
2 changes: 1 addition & 1 deletion aws/sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fun generateSmithyBuild(tests: List<AwsService>): String {
},
"service": "${it.service}",
"module": "aws-sdk-${it.module}",
"moduleVersion": "0.0.8-alpha",
"moduleVersion": "0.0.9-alpha",
"moduleAuthors": ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Russell Cohen <rcoh@amazon.com>"],
"license": "Apache-2.0"
${it.extraConfig ?: ""}
Expand Down
4 changes: 1 addition & 3 deletions aws/sdk/examples/lambda-invoke-function/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ async fn main() {
//
// For our example, we will simply print that the function doesn't
// exist and return a non-zero exit code to indicate the failure.
Err(SdkError::ServiceError { err, .. })
if matches!(err.kind, InvokeErrorKind::ResourceNotFoundError(_)) =>
{
Err(SdkError::ServiceError { err, .. }) if err.is_resource_not_found_error() => {
println!("This lambda function does not exist: {}", err);
process::exit(1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.documentShape
import software.amazon.smithy.rust.codegen.rustlang.rust
import software.amazon.smithy.rust.codegen.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate
import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata
import software.amazon.smithy.rust.codegen.util.toPascalCase
import software.amazon.smithy.rust.codegen.util.toSnakeCase
Expand All @@ -30,28 +31,43 @@ class UnionGenerator(

private val sortedMembers: List<MemberShape> = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) }
private fun renderUnion() {
val symbol = symbolProvider.toSymbol(shape)
val containerMeta = symbol.expectRustMetadata()
val unionSymbol = symbolProvider.toSymbol(shape)
val containerMeta = unionSymbol.expectRustMetadata()
containerMeta.render(writer)
writer.rustBlock("enum ${symbol.name}") {
writer.rustBlock("enum ${unionSymbol.name}") {
sortedMembers.forEach { member ->
val memberSymbol = symbolProvider.toSymbol(member)
documentShape(member, model)
memberSymbol.expectRustMetadata().renderAttributes(this)
write("${member.memberName.toPascalCase()}(#T),", symbolProvider.toSymbol(member))
}
}
writer.rustBlock("impl ${symbol.name}") {
writer.rustBlock("impl ${unionSymbol.name}") {
sortedMembers.forEach { member ->
val memberSymbol = symbolProvider.toSymbol(member)
val funcNamePart = member.memberName.toSnakeCase()
val variantName = member.memberName.toPascalCase()

writer.rustBlock("pub fn as_$funcNamePart(&self) -> Option<&#T>", memberSymbol) {
rust("if let ${symbol.name}::$variantName(val) = &self { Some(&val) } else { None }")
rustBlock("pub fn as_$funcNamePart(&self) -> Result<&#T, &Self>", memberSymbol) {
rust("if let ${unionSymbol.name}::$variantName(val) = &self { Ok(&val) } else { Err(&self) }")
}
writer.rustBlock("pub fn is_$funcNamePart(&self) -> bool") {
rust("self.as_$funcNamePart().is_some()")
rustBlock("pub fn is_$funcNamePart(&self) -> bool") {
rust("self.as_$funcNamePart().is_ok()")
}
}
}

sortedMembers.forEach { member ->
val memberSymbol = symbolProvider.toSymbol(member)
val variantName = member.memberName.toPascalCase()
writer.rustBlock("impl std::convert::TryFrom<${unionSymbol.name}> for #T", memberSymbol) {
rust("type Error = #T;", unionSymbol)
rustBlockTemplate(
"fn try_from(value: #{Union}) -> Result<#{Member}, Self::Error>",
"Union" to unionSymbol,
"Member" to memberSymbol
) {
rust("if let ${unionSymbol.name}::$variantName(variant) = value { Ok(variant) } else { Err(value) }")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.smithy.customize.Section
import software.amazon.smithy.rust.codegen.util.hasTrait
import software.amazon.smithy.rust.codegen.util.toSnakeCase

/**
* For a given Operation ([this]), return the symbol referring to the unified error? This can be used
Expand Down Expand Up @@ -119,14 +120,13 @@ class CombinedErrorGenerator(
}
}

writer.rustTemplate(
"""
impl ${symbol.name} {
writer.rustBlock("impl ${symbol.name}") {
writer.rustTemplate(
"""
pub fn new(kind: ${symbol.name}Kind, meta: #{generic_error}) -> Self {
Self { kind, meta }
}


pub fn unhandled(err: impl Into<Box<dyn #{std_error} + Send + Sync + 'static>>) -> Self {
Self {
kind: ${symbol.name}Kind::Unhandled(err.into()),
Expand Down Expand Up @@ -158,10 +158,17 @@ class CombinedErrorGenerator(
pub fn code(&self) -> Option<&str> {
self.meta.code()
}
}
""",
"generic_error" to genericError, "std_error" to RuntimeType.StdError
)
"generic_error" to genericError, "std_error" to RuntimeType.StdError
)
errors.forEach { error ->
val errorSymbol = symbolProvider.toSymbol(error)
val fnName = errorSymbol.name.toSnakeCase()
writer.rustBlock("pub fn is_$fnName(&self) -> bool") {
rust("matches!(&self.kind, ${symbol.name}Kind::${errorSymbol.name}(_))")
}
}
}

writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.StdError) {
rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ class UnionGeneratorTest {
let bar = MyUnion::IntValue(10);
assert_eq!(foo.is_string_value(), true);
assert_eq!(foo.is_int_value(), false);
assert_eq!(foo.as_string_value(), Some(&"foo".to_string()));
assert_eq!(foo.as_int_value(), None);
assert_eq!(foo.as_string_value(), Ok(&"foo".to_string()));
assert_eq!(foo.as_int_value(), Err(&foo));
assert_eq!(bar.is_string_value(), false);
assert_eq!(bar.is_int_value(), true);
assert_eq!(bar.as_string_value(), None);
assert_eq!(bar.as_int_value(), Some(&10));
assert_eq!(bar.as_string_value(), Err(&bar));
assert_eq!(bar.as_int_value(), Ok(&10));

use std::convert::TryFrom;
let _ = String::try_from(foo).expect("foo is a string");
"""
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ internal class CombinedErrorGeneratorTest {
use smithy_types::retry::ProvideErrorKind;
assert_eq!(error.retryable_error_kind(), Some(smithy_types::retry::ErrorKind::ClientError));

// Generate is_xyz methods for errors
assert_eq!(error.is_invalid_greeting(), true);
assert_eq!(error.is_complex_error(), false);

// unhandled variants properly delegate message
let error = GreetingError::generic(smithy_types::Error::builder().message("hello").build());
Expand Down