Unverified Commit 154dffd6 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Add a code generated `Unhandled` error (#1849)

parent 884f4418
Loading
Loading
Loading
Loading
+8 −9
Original line number Diff line number Diff line
@@ -166,9 +166,9 @@ class CombinedErrorGenerator(
            rust(
                """
                /// An unexpected error, e.g. invalid JSON returned by the service or an unknown error code
                Unhandled(Box<dyn #T + Send + Sync + 'static>),
                Unhandled(#T),
                """,
                RuntimeType.StdError,
                unhandledError(),
            )
        }
        writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) {
@@ -215,7 +215,7 @@ class CombinedErrorGenerator(
                /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type.
                pub fn unhandled(err: impl Into<Box<dyn #{std_error} + Send + Sync + 'static>>) -> Self {
                    Self {
                        kind: ${errorSymbol.name}Kind::Unhandled(err.into()),
                        kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())),
                        meta: Default::default()
                    }
                }
@@ -224,7 +224,7 @@ class CombinedErrorGenerator(
                pub fn generic(err: #{generic_error}) -> Self {
                    Self {
                        meta: err.clone(),
                        kind: ${errorSymbol.name}Kind::Unhandled(err.into()),
                        kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())),
                    }
                }

@@ -249,7 +249,9 @@ class CombinedErrorGenerator(
                    self.meta.code()
                }
                """,
                "generic_error" to genericError, "std_error" to RuntimeType.StdError,
                "generic_error" to genericError,
                "std_error" to RuntimeType.StdError,
                "Unhandled" to unhandledError(),
            )
            errors.forEach { error ->
                val errorVariantSymbol = symbolProvider.toSymbol(error)
@@ -265,10 +267,7 @@ class CombinedErrorGenerator(
            rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) {
                delegateToVariants(errors, errorSymbol) {
                    writable {
                        when (it) {
                            is VariantMatch.Unhandled -> rust("Some(_inner.as_ref())")
                            is VariantMatch.Modeled -> rust("Some(_inner)")
                        }
                        rust("Some(_inner)")
                    }
                }
            }
+12 −7
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.documentShape
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
@@ -92,17 +93,17 @@ class TopLevelErrorGenerator(private val codegenContext: CodegenContext, private
        }
    }

    private fun RustWriter.renderImplFrom(symbol: RuntimeType, errors: List<ShapeId>) {
    private fun RustWriter.renderImplFrom(errorSymbol: RuntimeType, errors: List<ShapeId>) {
        if (errors.isNotEmpty() || CodegenTarget.CLIENT == codegenContext.target) {
            rustBlock(
                "impl<R> From<#T<#T, R>> for Error where R: Send + Sync + std::fmt::Debug + 'static",
                sdkError,
                symbol,
                errorSymbol,
            ) {
                rustBlockTemplate(
                    "fn from(err: #{SdkError}<#{OpError}, R>) -> Self",
                    "SdkError" to sdkError,
                    "OpError" to symbol,
                    "OpError" to errorSymbol,
                ) {
                    rustBlock("match err") {
                        val operationErrors = errors.map { model.expectShape(it) }
@@ -111,12 +112,16 @@ class TopLevelErrorGenerator(private val codegenContext: CodegenContext, private
                                val errSymbol = symbolProvider.toSymbol(errorShape)
                                rust(
                                    "#TKind::${errSymbol.name}(inner) => Error::${errSymbol.name}(inner),",
                                    symbol,
                                    errorSymbol,
                                )
                            }
                            rust("#TKind::Unhandled(inner) => Error::Unhandled(inner),", symbol)
                            rustTemplate(
                                "#{errorSymbol}Kind::Unhandled(inner) => Error::Unhandled(#{unhandled}::new(inner.into())),",
                                "errorSymbol" to errorSymbol,
                                "unhandled" to unhandledError(),
                            )
                        }
                        rust("_ => Error::Unhandled(err.into()),")
                        rust("_ => Error::Unhandled(#T::new(err.into())),", unhandledError())
                    }
                }
            }
@@ -137,7 +142,7 @@ class TopLevelErrorGenerator(private val codegenContext: CodegenContext, private
                rust("${sym.name}(#T),", sym)
            }
            rust("/// An unhandled error occurred.")
            rust("Unhandled(Box<dyn #T + Send + Sync + 'static>)", RuntimeType.StdError)
            rust("Unhandled(#T)", unhandledError())
        }
    }
}
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.rust.codegen.core.smithy.generators.error

import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.docs
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType

internal fun unhandledError(): RuntimeType = RuntimeType.forInlineFun("Unhandled", RustModule.Error) {
    docs(
        """
        An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code)

        Call [`Error::source`](std::error::Error::source) for more details about the underlying cause.
        """,
    )
    rust("##[derive(Debug)]")
    rustBlock("pub struct Unhandled") {
        rust("source: Box<dyn #T + Send + Sync + 'static>", RuntimeType.StdError)
    }
    rustBlock("impl Unhandled") {
        rustBlock("pub(crate) fn new(source: Box<dyn #T + Send + Sync + 'static>) -> Self", RuntimeType.StdError) {
            rust("Self { source }")
        }
    }
    rustBlock("impl std::fmt::Display for Unhandled") {
        rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>") {
            rust("write!(f, \"unhandled error\")")
        }
    }
    rustBlock("impl std::error::Error for Unhandled") {
        rustBlock("fn source(&self) -> Option<&(dyn std::error::Error + 'static)>") {
            rust("Some(self.source.as_ref() as _)")
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -275,7 +275,9 @@ class EventStreamUnmarshallerGeneratorTest {
                    assert!(result.is_ok(), "expected ok, got: {:?}", result);
                    match expect_error(result.unwrap())$kindSuffix {
                        TestStreamErrorKind::Unhandled(err) => {
                            assert!(format!("{}", err).contains("message: \"unmodeled error\""));
                            let message = format!("{}", aws_smithy_types::error::display::DisplayErrorContext(&err));
                            let expected = "message: \"unmodeled error\"";
                            assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'");
                        }
                        kind => panic!("expected generic error, but got {:?}", kind),
                    }