in codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt [58:199]
fun render(writer: RustWriter) {
val errors = operationIndex.getErrors(operation)
val operationSymbol = symbolProvider.toSymbol(operation)
val symbol = operation.errorSymbol(symbolProvider)
val meta = RustMetadata(
derives = Attribute.Derives(setOf(RuntimeType.Debug)),
additionalAttributes = listOf(Attribute.NonExhaustive),
public = true
)
writer.rust("/// Error type for the `${operationSymbol.name}` operation.")
meta.render(writer)
writer.rustBlock("struct ${symbol.name}") {
rust(
"""
/// Kind of error that occurred.
pub kind: ${symbol.name}Kind,
/// Additional metadata about the error, including error code, message, and request ID.
pub (crate) meta: #T
""",
RuntimeType.GenericError(runtimeConfig)
)
}
writer.rust("/// Types of errors that can occur for the `${operationSymbol.name}` operation.")
meta.render(writer)
writer.rustBlock("enum ${symbol.name}Kind") {
errors.forEach { errorVariant ->
documentShape(errorVariant, model)
val errorSymbol = symbolProvider.toSymbol(errorVariant)
write("${errorSymbol.name}(#T),", errorSymbol)
}
rust(
"""
/// An unexpected error, e.g. invalid JSON returned by the service or an unknown error code
Unhandled(Box<dyn #T + Send + Sync + 'static>),
""",
RuntimeType.StdError
)
}
writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.stdfmt.member("Display")) {
rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") {
delegateToVariants {
writable { rust("_inner.fmt(f)") }
}
}
}
val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig)
writer.rustBlock(
"impl #T for ${symbol.name}",
RuntimeType.provideErrorKind(symbolProvider.config().runtimeConfig)
) {
rustBlock("fn code(&self) -> Option<&str>") {
rust("${symbol.name}::code(self)")
}
rustBlock("fn retryable_error_kind(&self) -> Option<#T>", errorKindT) {
val retryableVariants = errors.filter { it.hasTrait<RetryableTrait>() }
if (retryableVariants.isEmpty()) {
rust("None")
} else {
rustBlock("match &self.kind") {
retryableVariants.forEach {
val errorSymbol = symbolProvider.toSymbol(it)
rust("${symbol.name}Kind::${errorSymbol.name}(inner) => Some(inner.retryable_error_kind()),")
}
rust("_ => None")
}
}
}
}
writer.rustBlock("impl ${symbol.name}") {
writer.rustTemplate(
"""
/// Creates a new `${symbol.name}`.
pub fn new(kind: ${symbol.name}Kind, meta: #{generic_error}) -> Self {
Self { kind, meta }
}
/// Creates the `${symbol.name}::Unhandled` variant from any error type.
pub fn unhandled(err: impl Into<Box<dyn #{std_error} + Send + Sync + 'static>>) -> Self {
Self {
kind: ${symbol.name}Kind::Unhandled(err.into()),
meta: Default::default()
}
}
/// Creates the `${symbol.name}::Unhandled` variant from a `#{generic_error}`.
pub fn generic(err: #{generic_error}) -> Self {
Self {
meta: err.clone(),
kind: ${symbol.name}Kind::Unhandled(err.into()),
}
}
/// Returns the error message if one is available.
pub fn message(&self) -> Option<&str> {
self.meta.message()
}
/// Returns error metadata, which includes the error code, message,
/// request ID, and potentially additional information.
pub fn meta(&self) -> &#{generic_error} {
&self.meta
}
/// Returns the request ID if it's available.
pub fn request_id(&self) -> Option<&str> {
self.meta.request_id()
}
/// Returns the error code if it's available.
pub fn code(&self) -> Option<&str> {
self.meta.code()
}
""",
"generic_error" to genericError, "std_error" to RuntimeType.StdError
)
errors.forEach { error ->
val errorSymbol = symbolProvider.toSymbol(error)
val fnName = errorSymbol.name.toSnakeCase()
writer.rust("/// Returns `true` if the error kind is `${symbol.name}Kind::${errorSymbol.name}`.")
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) {
delegateToVariants {
writable {
when (it) {
is VariantMatch.Unhandled -> rust("Some(_inner.as_ref())")
is VariantMatch.Modeled -> rust("Some(_inner)")
}
}
}
}
}
}