mirror of https://github.com/smithy-lang/smithy-rs
Revise unhandled error variant according to RFC-39 (#3191)
This PR implements [RFC-39](https://github.com/smithy-lang/smithy-rs/blob/main/design/src/rfcs/rfc0039_forward_compatible_errors.md) with a couple slight deviations: - No `introspect` method is added since `Error` already implements `ProvideErrorMetadata`. - The same opaqueness and deprecation pointer is applied to the enum unknown variant for consistency. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
This commit is contained in:
parent
c0f72fbfe8
commit
c830caa281
|
@ -119,3 +119,61 @@ message = "Remove deprecated error kind type aliases."
|
||||||
references = ["smithy-rs#3189"]
|
references = ["smithy-rs#3189"]
|
||||||
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
|
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
|
||||||
author = "jdisanti"
|
author = "jdisanti"
|
||||||
|
|
||||||
|
[[aws-sdk-rust]]
|
||||||
|
message = """
|
||||||
|
Unhandled errors have been made opaque to ensure code is written in a future-proof manner. Where previously, you
|
||||||
|
might have:
|
||||||
|
```rust
|
||||||
|
match service_error.err() {
|
||||||
|
GetStorageError::StorageAccessNotAuthorized(_) => { /* ... */ }
|
||||||
|
GetStorageError::Unhandled(unhandled) if unhandled.code() == Some("SomeUnmodeledErrorCode") {
|
||||||
|
// unhandled error handling
|
||||||
|
}
|
||||||
|
_ => { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
It should now look as follows:
|
||||||
|
```rust
|
||||||
|
match service_error.err() {
|
||||||
|
GetStorageError::StorageAccessNotAuthorized(_) => { /* ... */ }
|
||||||
|
err if err.code() == Some("SomeUnmodeledErrorCode") {
|
||||||
|
// unhandled error handling
|
||||||
|
}
|
||||||
|
_ => { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Unhandled` variant should never be referenced directly.
|
||||||
|
"""
|
||||||
|
references = ["smithy-rs#3191"]
|
||||||
|
meta = { "breaking" = true, "tada" = false, "bug" = false }
|
||||||
|
author = "jdisanti"
|
||||||
|
|
||||||
|
[[smithy-rs]]
|
||||||
|
message = """
|
||||||
|
Unhandled errors have been made opaque to ensure code is written in a future-proof manner. Where previously, you
|
||||||
|
might have:
|
||||||
|
```rust
|
||||||
|
match service_error.err() {
|
||||||
|
GetStorageError::StorageAccessNotAuthorized(_) => { /* ... */ }
|
||||||
|
GetStorageError::Unhandled(unhandled) if unhandled.code() == Some("SomeUnmodeledErrorCode") {
|
||||||
|
// unhandled error handling
|
||||||
|
}
|
||||||
|
_ => { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
It should now look as follows:
|
||||||
|
```rust
|
||||||
|
match service_error.err() {
|
||||||
|
GetStorageError::StorageAccessNotAuthorized(_) => { /* ... */ }
|
||||||
|
err if err.code() == Some("SomeUnmodeledErrorCode") {
|
||||||
|
// unhandled error handling
|
||||||
|
}
|
||||||
|
_ => { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Unhandled` variant should never be referenced directly.
|
||||||
|
"""
|
||||||
|
references = ["smithy-rs#3191"]
|
||||||
|
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
|
||||||
|
author = "jdisanti"
|
||||||
|
|
|
@ -8,6 +8,7 @@ use aws_smithy_runtime_api::http::{Headers, Response};
|
||||||
use aws_smithy_types::error::metadata::{
|
use aws_smithy_types::error::metadata::{
|
||||||
Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata,
|
Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata,
|
||||||
};
|
};
|
||||||
|
#[allow(deprecated)]
|
||||||
use aws_smithy_types::error::Unhandled;
|
use aws_smithy_types::error::Unhandled;
|
||||||
|
|
||||||
const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id";
|
const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id";
|
||||||
|
@ -36,6 +37,7 @@ impl RequestIdExt for ErrorMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl RequestIdExt for Unhandled {
|
impl RequestIdExt for Unhandled {
|
||||||
fn extended_request_id(&self) -> Option<&str> {
|
fn extended_request_id(&self) -> Option<&str> {
|
||||||
self.meta().extended_request_id()
|
self.meta().extended_request_id()
|
||||||
|
|
|
@ -11,6 +11,8 @@ use aws_smithy_runtime_api::http::Response;
|
||||||
use aws_smithy_types::error::metadata::{
|
use aws_smithy_types::error::metadata::{
|
||||||
Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata,
|
Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
use aws_smithy_types::error::Unhandled;
|
use aws_smithy_types::error::Unhandled;
|
||||||
|
|
||||||
/// Constant for the [`ErrorMetadata`] extra field that contains the request ID
|
/// Constant for the [`ErrorMetadata`] extra field that contains the request ID
|
||||||
|
@ -38,6 +40,7 @@ impl RequestId for ErrorMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl RequestId for Unhandled {
|
impl RequestId for Unhandled {
|
||||||
fn request_id(&self) -> Option<&str> {
|
fn request_id(&self) -> Option<&str> {
|
||||||
self.meta().request_id()
|
self.meta().request_id()
|
||||||
|
|
|
@ -146,7 +146,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator {
|
||||||
val sym = codegenContext.symbolProvider.toSymbol(error)
|
val sym = codegenContext.symbolProvider.toSymbol(error)
|
||||||
rust("Self::${sym.name}(e) => #T,", wrapped)
|
rust("Self::${sym.name}(e) => #T,", wrapped)
|
||||||
}
|
}
|
||||||
rust("Self::Unhandled(e) => e.$accessorFunctionName(),")
|
rust("Self::Unhandled(e) => e.meta.$accessorFunctionName(),")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use aws_sdk_lambda::operation::RequestId;
|
||||||
use aws_sdk_lambda::{Client, Config};
|
use aws_sdk_lambda::{Client, Config};
|
||||||
use aws_smithy_runtime::client::http::test_util::infallible_client_fn;
|
use aws_smithy_runtime::client::http::test_util::infallible_client_fn;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn run_test(
|
async fn run_test(
|
||||||
response: impl Fn() -> http::Response<&'static str> + Send + Sync + 'static,
|
response: impl Fn() -> http::Response<&'static str> + Send + Sync + 'static,
|
||||||
expect_error: bool,
|
expect_error: bool,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
use aws_credential_types::provider::SharedCredentialsProvider;
|
use aws_credential_types::provider::SharedCredentialsProvider;
|
||||||
use aws_sdk_s3::config::{Credentials, Region};
|
use aws_sdk_s3::config::{Credentials, Region};
|
||||||
|
use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Error;
|
||||||
use aws_sdk_s3::{Client, Config};
|
use aws_sdk_s3::{Client, Config};
|
||||||
use aws_smithy_runtime::client::http::test_util::capture_request;
|
use aws_smithy_runtime::client::http::test_util::capture_request;
|
||||||
|
|
||||||
|
@ -58,8 +59,8 @@ async fn test_s3_signer_query_string_with_all_valid_chars() {
|
||||||
// test must be run against an actual bucket so we `ignore` it unless the runner specifically requests it
|
// test must be run against an actual bucket so we `ignore` it unless the runner specifically requests it
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn test_query_strings_are_correctly_encoded() {
|
async fn test_query_strings_are_correctly_encoded() {
|
||||||
use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Error;
|
|
||||||
use aws_smithy_runtime_api::client::result::SdkError;
|
use aws_smithy_runtime_api::client::result::SdkError;
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
@ -80,22 +81,19 @@ async fn test_query_strings_are_correctly_encoded() {
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
if let Err(SdkError::ServiceError(context)) = res {
|
if let Err(SdkError::ServiceError(context)) = res {
|
||||||
match context.err() {
|
let err = context.err();
|
||||||
ListObjectsV2Error::Unhandled(e)
|
let msg = err.to_string();
|
||||||
if e.to_string().contains("SignatureDoesNotMatch") =>
|
let unhandled = matches!(err, ListObjectsV2Error::Unhandled(_));
|
||||||
{
|
if unhandled && msg.contains("SignatureDoesNotMatch") {
|
||||||
chars_that_break_signing.push(byte);
|
chars_that_break_signing.push(byte);
|
||||||
}
|
} else if unhandled && msg.to_string().contains("InvalidUri") {
|
||||||
ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidUri") => {
|
chars_that_break_uri_parsing.push(byte);
|
||||||
chars_that_break_uri_parsing.push(byte);
|
} else if unhandled && msg.to_string().contains("InvalidArgument") {
|
||||||
}
|
chars_that_are_invalid_arguments.push(byte);
|
||||||
ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidArgument") => {
|
} else if unhandled && msg.to_string().contains("InvalidToken") {
|
||||||
chars_that_are_invalid_arguments.push(byte);
|
panic!("refresh your credentials and run this test again");
|
||||||
}
|
} else {
|
||||||
ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidToken") => {
|
todo!("unexpected error: {:?}", err);
|
||||||
panic!("refresh your credentials and run this test again");
|
|
||||||
}
|
|
||||||
e => todo!("unexpected error: {:?}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ async fn get_request_id_from_modeled_error() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn get_request_id_from_unmodeled_error() {
|
async fn get_request_id_from_unmodeled_error() {
|
||||||
let (http_client, request) = capture_request(Some(
|
let (http_client, request) = capture_request(Some(
|
||||||
http::Response::builder()
|
http::Response::builder()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
|
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
|
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
|
||||||
|
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
|
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.docs
|
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.rust
|
||||||
|
@ -75,12 +76,39 @@ data class InfallibleEnumType(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun additionalEnumImpls(context: EnumGeneratorContext): Writable = writable {
|
||||||
|
// `try_parse` isn't needed for unnamed enums
|
||||||
|
if (context.enumTrait.hasNames()) {
|
||||||
|
rustTemplate(
|
||||||
|
"""
|
||||||
|
impl ${context.enumName} {
|
||||||
|
/// Parses the enum value while disallowing unknown variants.
|
||||||
|
///
|
||||||
|
/// Unknown variants will result in an error.
|
||||||
|
pub fn try_parse(value: &str) -> #{Result}<Self, #{UnknownVariantError}> {
|
||||||
|
match Self::from(value) {
|
||||||
|
##[allow(deprecated)]
|
||||||
|
Self::Unknown(_) => #{Err}(#{UnknownVariantError}::new(value)),
|
||||||
|
known => Ok(known),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
*preludeScope,
|
||||||
|
"UnknownVariantError" to unknownVariantError(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun additionalDocs(context: EnumGeneratorContext): Writable = writable {
|
override fun additionalDocs(context: EnumGeneratorContext): Writable = writable {
|
||||||
renderForwardCompatibilityNote(context.enumName, context.sortedMembers, UnknownVariant, UnknownVariantValue)
|
renderForwardCompatibilityNote(context.enumName, context.sortedMembers, UnknownVariant, UnknownVariantValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun additionalEnumMembers(context: EnumGeneratorContext): Writable = writable {
|
override fun additionalEnumMembers(context: EnumGeneratorContext): Writable = writable {
|
||||||
docs("`$UnknownVariant` contains new variants that have been added since this code was generated.")
|
docs("`$UnknownVariant` contains new variants that have been added since this code was generated.")
|
||||||
|
rust(
|
||||||
|
"""##[deprecated(note = "Don't directly match on `$UnknownVariant`. See the docs on this enum for the correct way to handle unknown variants.")]""",
|
||||||
|
)
|
||||||
rust("$UnknownVariant(#T)", unknownVariantValue(context))
|
rust("$UnknownVariant(#T)", unknownVariantValue(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +121,9 @@ data class InfallibleEnumType(
|
||||||
docs(
|
docs(
|
||||||
"""
|
"""
|
||||||
Opaque struct used as inner data for the `Unknown` variant defined in enums in
|
Opaque struct used as inner data for the `Unknown` variant defined in enums in
|
||||||
the crate
|
the crate.
|
||||||
|
|
||||||
While this is not intended to be used directly, it is marked as `pub` because it is
|
This is not intended to be used directly.
|
||||||
part of the enums that are public interface.
|
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
)
|
)
|
||||||
context.enumMeta.render(this)
|
context.enumMeta.render(this)
|
||||||
|
@ -174,5 +201,35 @@ class ClientEnumGenerator(codegenContext: ClientCodegenContext, shape: StringSha
|
||||||
codegenContext.model,
|
codegenContext.model,
|
||||||
codegenContext.symbolProvider,
|
codegenContext.symbolProvider,
|
||||||
shape,
|
shape,
|
||||||
InfallibleEnumType(ClientRustModule.primitives),
|
InfallibleEnumType(
|
||||||
|
RustModule.new(
|
||||||
|
"sealed_enum_unknown",
|
||||||
|
visibility = Visibility.PUBCRATE,
|
||||||
|
parent = ClientRustModule.primitives,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun unknownVariantError(): RuntimeType = RuntimeType.forInlineFun("UnknownVariantError", ClientRustModule.Error) {
|
||||||
|
rustTemplate(
|
||||||
|
"""
|
||||||
|
/// The given enum value failed to parse since it is not a known value.
|
||||||
|
##[derive(Debug)]
|
||||||
|
pub struct UnknownVariantError {
|
||||||
|
value: #{String},
|
||||||
|
}
|
||||||
|
impl UnknownVariantError {
|
||||||
|
pub(crate) fn new(value: impl #{Into}<#{String}>) -> Self {
|
||||||
|
Self { value: value.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ::std::fmt::Display for UnknownVariantError {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> #{Result}<(), ::std::fmt::Error> {
|
||||||
|
write!(f, "unknown enum variant: '{}'", self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ::std::error::Error for UnknownVariantError {}
|
||||||
|
""",
|
||||||
|
*preludeScope,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.errorMetadata
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.errorMetadata
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError
|
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
|
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.customize.Section
|
import software.amazon.smithy.rust.codegen.core.smithy.customize.Section
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations
|
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations
|
||||||
|
@ -83,12 +82,14 @@ class OperationErrorGenerator(
|
||||||
val errorVariantSymbol = symbolProvider.toSymbol(errorVariant)
|
val errorVariantSymbol = symbolProvider.toSymbol(errorVariant)
|
||||||
write("${errorVariantSymbol.name}(#T),", errorVariantSymbol)
|
write("${errorVariantSymbol.name}(#T),", errorVariantSymbol)
|
||||||
}
|
}
|
||||||
rust(
|
rustTemplate(
|
||||||
"""
|
"""
|
||||||
/// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).
|
/// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).
|
||||||
Unhandled(#T),
|
#{deprecation}
|
||||||
|
Unhandled(#{Unhandled}),
|
||||||
""",
|
""",
|
||||||
unhandledError(runtimeConfig),
|
"deprecation" to writable { renderUnhandledErrorDeprecation(runtimeConfig, errorSymbol.name) },
|
||||||
|
"Unhandled" to unhandledError(runtimeConfig),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,15 +115,9 @@ class OperationErrorGenerator(
|
||||||
"StdError" to RuntimeType.StdError,
|
"StdError" to RuntimeType.StdError,
|
||||||
"ErrorMeta" to errorMetadata,
|
"ErrorMeta" to errorMetadata,
|
||||||
) {
|
) {
|
||||||
rust(
|
rustTemplate(
|
||||||
"""
|
"""Self::Unhandled(#{Unhandled} { source, meta: meta.unwrap_or_default() })""",
|
||||||
Self::Unhandled({
|
"Unhandled" to unhandledError(runtimeConfig),
|
||||||
let mut builder = #T::builder().source(source);
|
|
||||||
builder.set_meta(meta);
|
|
||||||
builder.build()
|
|
||||||
})
|
|
||||||
""",
|
|
||||||
unhandledError(runtimeConfig),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,8 +126,23 @@ class OperationErrorGenerator(
|
||||||
private fun RustWriter.renderImplDisplay(errorSymbol: Symbol, errors: List<StructureShape>) {
|
private fun RustWriter.renderImplDisplay(errorSymbol: Symbol, errors: List<StructureShape>) {
|
||||||
rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) {
|
rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) {
|
||||||
rustBlock("fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result") {
|
rustBlock("fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result") {
|
||||||
delegateToVariants(errors) {
|
delegateToVariants(errors) { variantMatch ->
|
||||||
writable { rust("_inner.fmt(f)") }
|
when (variantMatch) {
|
||||||
|
is VariantMatch.Unhandled -> writable {
|
||||||
|
rustTemplate(
|
||||||
|
"""
|
||||||
|
if let #{Some}(code) = #{ProvideErrorMetadata}::code(self) {
|
||||||
|
write!(f, "unhandled error ({code})")
|
||||||
|
} else {
|
||||||
|
f.write_str("unhandled error")
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
*preludeScope,
|
||||||
|
"ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(runtimeConfig),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is VariantMatch.Modeled -> writable { rust("_inner.fmt(f)") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,8 +152,13 @@ class OperationErrorGenerator(
|
||||||
val errorMetadataTrait = RuntimeType.provideErrorMetadataTrait(runtimeConfig)
|
val errorMetadataTrait = RuntimeType.provideErrorMetadataTrait(runtimeConfig)
|
||||||
rustBlock("impl #T for ${errorSymbol.name}", errorMetadataTrait) {
|
rustBlock("impl #T for ${errorSymbol.name}", errorMetadataTrait) {
|
||||||
rustBlock("fn meta(&self) -> &#T", errorMetadata(runtimeConfig)) {
|
rustBlock("fn meta(&self) -> &#T", errorMetadata(runtimeConfig)) {
|
||||||
delegateToVariants(errors) {
|
delegateToVariants(errors) { variantMatch ->
|
||||||
writable { rust("#T::meta(_inner)", errorMetadataTrait) }
|
writable {
|
||||||
|
when (variantMatch) {
|
||||||
|
is VariantMatch.Unhandled -> rust("&_inner.meta")
|
||||||
|
is VariantMatch.Modeled -> rust("#T::meta(_inner)", errorMetadataTrait)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,16 +204,16 @@ class OperationErrorGenerator(
|
||||||
"""
|
"""
|
||||||
/// Creates the `${errorSymbol.name}::Unhandled` variant from any error type.
|
/// Creates the `${errorSymbol.name}::Unhandled` variant from any error type.
|
||||||
pub fn unhandled(err: impl #{Into}<#{Box}<dyn #{StdError} + #{Send} + #{Sync} + 'static>>) -> Self {
|
pub fn unhandled(err: impl #{Into}<#{Box}<dyn #{StdError} + #{Send} + #{Sync} + 'static>>) -> Self {
|
||||||
Self::Unhandled(#{Unhandled}::builder().source(err).build())
|
Self::Unhandled(#{Unhandled} { source: err.into(), meta: #{Default}::default() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the `${errorSymbol.name}::Unhandled` variant from a `#{error_metadata}`.
|
/// Creates the `${errorSymbol.name}::Unhandled` variant from an [`ErrorMetadata`](#{ErrorMetadata}).
|
||||||
pub fn generic(err: #{error_metadata}) -> Self {
|
pub fn generic(err: #{ErrorMetadata}) -> Self {
|
||||||
Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build())
|
Self::Unhandled(#{Unhandled} { source: err.clone().into(), meta: err })
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
*preludeScope,
|
*preludeScope,
|
||||||
"error_metadata" to errorMetadata,
|
"ErrorMetadata" to errorMetadata,
|
||||||
"StdError" to RuntimeType.StdError,
|
"StdError" to RuntimeType.StdError,
|
||||||
"Unhandled" to unhandledError(runtimeConfig),
|
"Unhandled" to unhandledError(runtimeConfig),
|
||||||
)
|
)
|
||||||
|
@ -209,13 +224,15 @@ class OperationErrorGenerator(
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
rustBlock("pub fn meta(&self) -> &#T", errorMetadata) {
|
rustBlock("pub fn meta(&self) -> &#T", errorMetadata) {
|
||||||
rust("use #T;", RuntimeType.provideErrorMetadataTrait(runtimeConfig))
|
|
||||||
rustBlock("match self") {
|
rustBlock("match self") {
|
||||||
errors.forEach { error ->
|
errors.forEach { error ->
|
||||||
val errorVariantSymbol = symbolProvider.toSymbol(error)
|
val errorVariantSymbol = symbolProvider.toSymbol(error)
|
||||||
rust("Self::${errorVariantSymbol.name}(e) => e.meta(),")
|
rustTemplate(
|
||||||
|
"Self::${errorVariantSymbol.name}(e) => #{ProvideErrorMetadata}::meta(e),",
|
||||||
|
"ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(runtimeConfig),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
rust("Self::Unhandled(e) => e.meta(),")
|
rust("Self::Unhandled(e) => &e.meta,")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errors.forEach { error ->
|
errors.forEach { error ->
|
||||||
|
@ -236,9 +253,14 @@ class OperationErrorGenerator(
|
||||||
*preludeScope,
|
*preludeScope,
|
||||||
"StdError" to RuntimeType.StdError,
|
"StdError" to RuntimeType.StdError,
|
||||||
) {
|
) {
|
||||||
delegateToVariants(errors) {
|
delegateToVariants(errors) { variantMatch ->
|
||||||
writable {
|
when (variantMatch) {
|
||||||
rustTemplate("#{Some}(_inner)", *preludeScope)
|
is VariantMatch.Unhandled -> writable {
|
||||||
|
rustTemplate("#{Some}(&*_inner.source)", *preludeScope)
|
||||||
|
}
|
||||||
|
is VariantMatch.Modeled -> writable {
|
||||||
|
rustTemplate("#{Some}(_inner)", *preludeScope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import software.amazon.smithy.codegen.core.Symbol
|
||||||
import software.amazon.smithy.model.shapes.OperationShape
|
import software.amazon.smithy.model.shapes.OperationShape
|
||||||
import software.amazon.smithy.model.shapes.ShapeId
|
import software.amazon.smithy.model.shapes.ShapeId
|
||||||
import software.amazon.smithy.model.shapes.StructureShape
|
import software.amazon.smithy.model.shapes.StructureShape
|
||||||
|
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
|
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata
|
import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
|
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
|
||||||
|
@ -24,9 +25,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||||
import software.amazon.smithy.rust.codegen.core.rustlang.writable
|
import software.amazon.smithy.rust.codegen.core.rustlang.writable
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
|
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.CodegenTarget
|
||||||
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
|
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError
|
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
|
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations
|
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations
|
||||||
import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError
|
import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError
|
||||||
|
@ -91,7 +92,7 @@ class ServiceErrorGenerator(
|
||||||
allErrors.forEach {
|
allErrors.forEach {
|
||||||
rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.source(),")
|
rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.source(),")
|
||||||
}
|
}
|
||||||
rust("Error::Unhandled(inner) => inner.source()")
|
rustTemplate("Error::Unhandled(inner) => #{Some}(&*inner.source)", *preludeScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,17 @@ class ServiceErrorGenerator(
|
||||||
allErrors.forEach {
|
allErrors.forEach {
|
||||||
rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.fmt(f),")
|
rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.fmt(f),")
|
||||||
}
|
}
|
||||||
rust("Error::Unhandled(inner) => inner.fmt(f)")
|
rustTemplate(
|
||||||
|
"""
|
||||||
|
Error::Unhandled(_) => if let #{Some}(code) = #{ProvideErrorMetadata}::code(self) {
|
||||||
|
write!(f, "unhandled error ({code})")
|
||||||
|
} else {
|
||||||
|
f.write_str("unhandled error")
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
*preludeScope,
|
||||||
|
"ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(codegenContext.runtimeConfig),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,11 +129,12 @@ class ServiceErrorGenerator(
|
||||||
"""
|
"""
|
||||||
impl From<#{BuildError}> for Error {
|
impl From<#{BuildError}> for Error {
|
||||||
fn from(value: #{BuildError}) -> Self {
|
fn from(value: #{BuildError}) -> Self {
|
||||||
Error::Unhandled(#{Unhandled}::builder().source(value).build())
|
Error::Unhandled(#{Unhandled} { source: value.into(), meta: #{Default}::default() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
""",
|
""",
|
||||||
|
*preludeScope,
|
||||||
"BuildError" to codegenContext.runtimeConfig.operationBuildError(),
|
"BuildError" to codegenContext.runtimeConfig.operationBuildError(),
|
||||||
"Unhandled" to unhandledError(codegenContext.runtimeConfig),
|
"Unhandled" to unhandledError(codegenContext.runtimeConfig),
|
||||||
)
|
)
|
||||||
|
@ -146,10 +158,10 @@ class ServiceErrorGenerator(
|
||||||
rustTemplate(
|
rustTemplate(
|
||||||
"""
|
"""
|
||||||
_ => Error::Unhandled(
|
_ => Error::Unhandled(
|
||||||
#{Unhandled}::builder()
|
#{Unhandled} {
|
||||||
.meta(#{ProvideErrorMetadata}::meta(&err).clone())
|
meta: #{ProvideErrorMetadata}::meta(&err).clone(),
|
||||||
.source(err)
|
source: err.into(),
|
||||||
.build()
|
}
|
||||||
),
|
),
|
||||||
""",
|
""",
|
||||||
"Unhandled" to unhandledError(codegenContext.runtimeConfig),
|
"Unhandled" to unhandledError(codegenContext.runtimeConfig),
|
||||||
|
@ -187,7 +199,7 @@ class ServiceErrorGenerator(
|
||||||
fn meta(&self) -> &#{ErrorMetadata} {
|
fn meta(&self) -> &#{ErrorMetadata} {
|
||||||
match self {
|
match self {
|
||||||
#{matchers}
|
#{matchers}
|
||||||
Self::Unhandled(inner) => inner.meta(),
|
Self::Unhandled(inner) => &inner.meta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +232,57 @@ class ServiceErrorGenerator(
|
||||||
rust("${sym.name}(#T),", sym)
|
rust("${sym.name}(#T),", sym)
|
||||||
}
|
}
|
||||||
docs("An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).")
|
docs("An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).")
|
||||||
|
renderUnhandledErrorDeprecation(codegenContext.runtimeConfig, "Error")
|
||||||
rust("Unhandled(#T)", unhandledError(codegenContext.runtimeConfig))
|
rust("Unhandled(#T)", unhandledError(codegenContext.runtimeConfig))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unhandledError(rc: RuntimeConfig): RuntimeType = RuntimeType.forInlineFun(
|
||||||
|
"Unhandled",
|
||||||
|
// Place in a sealed module so that it can't be referenced at all
|
||||||
|
RustModule.pubCrate("sealed_unhandled", ClientRustModule.Error),
|
||||||
|
) {
|
||||||
|
rustTemplate(
|
||||||
|
"""
|
||||||
|
/// This struct is not intended to be used.
|
||||||
|
///
|
||||||
|
/// This struct holds information about an unhandled error,
|
||||||
|
/// but that information should be obtained by using the
|
||||||
|
/// [`ProvideErrorMetadata`](#{ProvideErrorMetadata}) trait
|
||||||
|
/// on the error type.
|
||||||
|
///
|
||||||
|
/// This struct intentionally doesn't yield any useful information itself.
|
||||||
|
#{deprecation}
|
||||||
|
##[derive(Debug)]
|
||||||
|
pub struct Unhandled {
|
||||||
|
pub(crate) source: #{BoxError},
|
||||||
|
pub(crate) meta: #{ErrorMetadata},
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"BoxError" to RuntimeType.smithyRuntimeApi(rc).resolve("box_error::BoxError"),
|
||||||
|
"deprecation" to writable { renderUnhandledErrorDeprecation(rc) },
|
||||||
|
"ErrorMetadata" to RuntimeType.smithyTypes(rc).resolve("error::metadata::ErrorMetadata"),
|
||||||
|
"ProvideErrorMetadata" to RuntimeType.smithyTypes(rc).resolve("error::metadata::ProvideErrorMetadata"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RustWriter.renderUnhandledErrorDeprecation(rc: RuntimeConfig, errorName: String? = null) {
|
||||||
|
val link = if (errorName != null) {
|
||||||
|
"##impl-ProvideErrorMetadata-for-$errorName"
|
||||||
|
} else {
|
||||||
|
"#{ProvideErrorMetadata}"
|
||||||
|
}
|
||||||
|
val message = """
|
||||||
|
Matching `Unhandled` directly is not forwards compatible. Instead, match using a
|
||||||
|
variable wildcard pattern and check `.code()`:<br/>
|
||||||
|
`err if err.code() == Some("SpecificExceptionCode") => { /* handle the error */ }`<br/>
|
||||||
|
See [`ProvideErrorMetadata`]($link) for what information is available for the error.
|
||||||
|
""".trimIndent()
|
||||||
|
// `.dq()` doesn't quite do what we want here since we actually want a Rust multi-line string
|
||||||
|
val messageEscaped = message.replace("\"", "\\\"").replace("\n", " \\\n").replace("<br/>", "\n")
|
||||||
|
rustTemplate(
|
||||||
|
"""##[deprecated(note = "$messageEscaped")]""",
|
||||||
|
"ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(rc),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -119,7 +119,10 @@ class ClientEnumGeneratorTest {
|
||||||
"""
|
"""
|
||||||
assert_eq!(SomeEnum::from("Unknown"), SomeEnum::UnknownValue);
|
assert_eq!(SomeEnum::from("Unknown"), SomeEnum::UnknownValue);
|
||||||
assert_eq!(SomeEnum::from("UnknownValue"), SomeEnum::UnknownValue_);
|
assert_eq!(SomeEnum::from("UnknownValue"), SomeEnum::UnknownValue_);
|
||||||
assert_eq!(SomeEnum::from("SomethingNew"), SomeEnum::Unknown(crate::primitives::UnknownVariantValue("SomethingNew".to_owned())));
|
assert_eq!(
|
||||||
|
SomeEnum::from("SomethingNew"),
|
||||||
|
SomeEnum::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue("SomethingNew".to_owned()))
|
||||||
|
);
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -150,7 +153,10 @@ class ClientEnumGeneratorTest {
|
||||||
assert_eq!(instance.as_str(), "t2.micro");
|
assert_eq!(instance.as_str(), "t2.micro");
|
||||||
assert_eq!(InstanceType::from("t2.nano"), InstanceType::T2Nano);
|
assert_eq!(InstanceType::from("t2.nano"), InstanceType::T2Nano);
|
||||||
// round trip unknown variants:
|
// round trip unknown variants:
|
||||||
assert_eq!(InstanceType::from("other"), InstanceType::Unknown(crate::primitives::UnknownVariantValue("other".to_owned())));
|
assert_eq!(
|
||||||
|
InstanceType::from("other"),
|
||||||
|
InstanceType::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue("other".to_owned()))
|
||||||
|
);
|
||||||
assert_eq!(InstanceType::from("other").as_str(), "other");
|
assert_eq!(InstanceType::from("other").as_str(), "other");
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ClientEventStreamUnmarshallerGeneratorTest {
|
||||||
let result = $generator::new().unmarshall(&message);
|
let result = $generator::new().unmarshall(&message);
|
||||||
assert!(result.is_ok(), "expected ok, got: {:?}", result);
|
assert!(result.is_ok(), "expected ok, got: {:?}", result);
|
||||||
match expect_error(result.unwrap()) {
|
match expect_error(result.unwrap()) {
|
||||||
TestStreamError::Unhandled(err) => {
|
err @ TestStreamError::Unhandled(_) => {
|
||||||
let message = format!("{}", crate::error::DisplayErrorContext(&err));
|
let message = format!("{}", crate::error::DisplayErrorContext(&err));
|
||||||
let expected = "message: \"unmodeled error\"";
|
let expected = "message: \"unmodeled error\"";
|
||||||
assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'");
|
assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'");
|
||||||
|
|
|
@ -425,7 +425,6 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
|
||||||
fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) =
|
fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) =
|
||||||
smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata")
|
smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata")
|
||||||
|
|
||||||
fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled")
|
|
||||||
fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig))
|
fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig))
|
||||||
fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) =
|
fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) =
|
||||||
forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig))
|
forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig))
|
||||||
|
|
|
@ -2,21 +2,20 @@
|
||||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
/// Copyright © 2023, Amazon, LLC.
|
|
||||||
///
|
//! This example demonstrates how to handle service generated errors.
|
||||||
/// This example demonstrates how to handle service generated errors.
|
//!
|
||||||
///
|
//! The example assumes that the Pokémon service is running on the localhost on TCP port 13734.
|
||||||
/// The example assumes that the Pokémon service is running on the localhost on TCP port 13734.
|
//! Refer to the [README.md](https://github.com/smithy-lang/smithy-rs/tree/main/examples/pokemon-service-client-usage/README.md)
|
||||||
/// Refer to the [README.md](https://github.com/smithy-lang/smithy-rs/tree/main/examples/pokemon-service-client-usage/README.md)
|
//! file for instructions on how to launch the service locally.
|
||||||
/// file for instructions on how to launch the service locally.
|
//!
|
||||||
///
|
//! The example can be run using `cargo run --example handling-errors`.
|
||||||
/// The example can be run using `cargo run --example handling-errors`.
|
|
||||||
///
|
use pokemon_service_client::error::DisplayErrorContext;
|
||||||
|
use pokemon_service_client::Client as PokemonClient;
|
||||||
use pokemon_service_client::{error::SdkError, operation::get_storage::GetStorageError};
|
use pokemon_service_client::{error::SdkError, operation::get_storage::GetStorageError};
|
||||||
use pokemon_service_client_usage::{setup_tracing_subscriber, POKEMON_SERVICE_URL};
|
use pokemon_service_client_usage::{setup_tracing_subscriber, POKEMON_SERVICE_URL};
|
||||||
|
|
||||||
use pokemon_service_client::Client as PokemonClient;
|
|
||||||
|
|
||||||
/// Creates a new `smithy-rs` client that is configured to communicate with a locally running Pokémon service on TCP port 13734.
|
/// Creates a new `smithy-rs` client that is configured to communicate with a locally running Pokémon service on TCP port 13734.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -77,14 +76,10 @@ async fn main() {
|
||||||
GetStorageError::ValidationError(ve) => {
|
GetStorageError::ValidationError(ve) => {
|
||||||
tracing::error!(error = %ve, "A required field has not been set.");
|
tracing::error!(error = %ve, "A required field has not been set.");
|
||||||
}
|
}
|
||||||
// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).
|
|
||||||
GetStorageError::Unhandled(uh) => {
|
|
||||||
tracing::error!(error = %uh, "An unhandled error has occurred.")
|
|
||||||
}
|
|
||||||
// The SdkError is marked as `#[non_exhaustive]`. Therefore, a catch-all pattern is required to handle
|
// The SdkError is marked as `#[non_exhaustive]`. Therefore, a catch-all pattern is required to handle
|
||||||
// potential future variants introduced in SdkError.
|
// potential future variants introduced in SdkError.
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!(error = %se.err(), "Some other error has occurred on the server")
|
tracing::error!(error = %DisplayErrorContext(se.err()), "Some other error has occurred on the server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,6 @@ allowed_external_types = [
|
||||||
"http::header::value::HeaderValue",
|
"http::header::value::HeaderValue",
|
||||||
"http::request::Request",
|
"http::request::Request",
|
||||||
"http::response::Response",
|
"http::response::Response",
|
||||||
|
"http::status::StatusCode",
|
||||||
"http::uri::Uri",
|
"http::uri::Uri",
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,4 +13,4 @@ mod response;
|
||||||
pub use error::HttpError;
|
pub use error::HttpError;
|
||||||
pub use headers::{HeaderValue, Headers, HeadersIter};
|
pub use headers::{HeaderValue, Headers, HeadersIter};
|
||||||
pub use request::{Request, RequestParts};
|
pub use request::{Request, RequestParts};
|
||||||
pub use response::Response;
|
pub use response::{Response, StatusCode};
|
||||||
|
|
|
@ -13,6 +13,8 @@ pub mod operation;
|
||||||
mod unhandled;
|
mod unhandled;
|
||||||
|
|
||||||
pub use metadata::ErrorMetadata;
|
pub use metadata::ErrorMetadata;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
pub use unhandled::Unhandled;
|
pub use unhandled::Unhandled;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
//! Unhandled error type.
|
//! Unhandled error type.
|
||||||
|
|
||||||
use crate::error::{metadata::ProvideErrorMetadata, ErrorMetadata};
|
use crate::error::{metadata::ProvideErrorMetadata, ErrorMetadata};
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
|
#[deprecated(note = "The `Unhandled` type is no longer used by errors.")]
|
||||||
/// Builder for [`Unhandled`]
|
/// Builder for [`Unhandled`]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
|
@ -58,6 +61,7 @@ impl Builder {
|
||||||
/// [`DisplayErrorContext`](crate::error::display::DisplayErrorContext), use another
|
/// [`DisplayErrorContext`](crate::error::display::DisplayErrorContext), use another
|
||||||
/// error reporter library that visits the error's cause/source chain, or call
|
/// error reporter library that visits the error's cause/source chain, or call
|
||||||
/// [`Error::source`](std::error::Error::source) for more details about the underlying cause.
|
/// [`Error::source`](std::error::Error::source) for more details about the underlying cause.
|
||||||
|
#[deprecated(note = "This type is no longer used by errors.")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Unhandled {
|
pub struct Unhandled {
|
||||||
source: Box<dyn StdError + Send + Sync + 'static>,
|
source: Box<dyn StdError + Send + Sync + 'static>,
|
||||||
|
|
Loading…
Reference in New Issue