diff --git a/CHANGELOG.md b/CHANGELOG.md index d3795df9..cd05b2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add a common Rust `ApiCallError` with conversions from every generated + operation error, while retaining operation-specific typed errors for callers + that need to match individual responses. - Preserve successful response headers across every generator. Python clients add `*_with_http_info()` methods while keeping existing body-only methods unchanged. diff --git a/docs/src/rust_config.md b/docs/src/rust_config.md index 97d15133..32d3b581 100644 --- a/docs/src/rust_config.md +++ b/docs/src/rust_config.md @@ -4,6 +4,81 @@ All three Rust backends (`rust-reqwest`, `rust-ureq`, `rust-aioduct`) share the The `rust-aioduct` backend has an additional `[aioduct]` section for controlling aioduct-specific features. +## Generated Error Handling + +Every operation keeps its generated error enum, so callers can match a specific status and use its typed body: + +```rust +match api.create_resource(&request).await { + Err(CreateResourceError::NotFound(error)) => { + if let Ok(body) = error.body() { + println!("missing resource: {}", body.message); + } + } + Err(error) => return Err(error.into()), + Ok(response) => use_response(response), +} +``` + +Each operation error also implements `Into`. Use the common type when a workflow calls multiple operations and does not need their distinct body types: + +```rust +use generated_sdk::runtime::error::ApiCallError; + +async fn run_workflow( + api: &ResourcesApi<'_>, + request: &CreateResourceRequest, + resource_id: &str, +) -> Result<(), ApiCallError> { + api.create_resource(request).await?; + api.refresh_resource(resource_id).await?; + Ok(()) +} +``` + +`ApiCallError` exposes the operation identifier (generated from the HTTP method and path if the OpenAPI document omits one) and optional HTTP status, native headers, and raw response body. Transport errors and error-body decoding failures remain available through `std::error::Error::source()`. + +Conversion consumes the operation-specific error and erases its decoded payload type. Match the operation error before conversion when that payload is needed. + +### `thiserror` + +`thiserror` does not chain two `From` conversions for `?`. A single generic adapter on the application error converts every generated operation error directly: + +```rust +#[derive(Debug, thiserror::Error)] +#[error("SDK request failed: {source}")] +struct AppError { + #[source] + source: ApiCallError, +} + +impl From for AppError +where + E: Into, +{ + fn from(error: E) -> Self { + Self { + source: error.into(), + } + } +} +``` + +Do not also put `#[from]` on the `ApiCallError` field; the generic implementation already includes `ApiCallError` itself. + +### SNAFU + +SNAFU 0.9's generic source conversion provides the same direct `?` behavior: + +```rust +#[derive(Debug, snafu::Snafu)] +#[snafu(context(false))] +struct AppError { + #[snafu(source(from(generic)))] + source: ApiCallError, +} +``` + ## Full Example ```toml diff --git a/src/generators/rust/aioduct/codegen.rs b/src/generators/rust/aioduct/codegen.rs index d8cbd957..ff6303cb 100644 --- a/src/generators/rust/aioduct/codegen.rs +++ b/src/generators/rust/aioduct/codegen.rs @@ -74,6 +74,7 @@ impl RustAioductCodeGenerator { files.extend(runtime_files( &header, aioduct_cfg, + !ir.operations.is_empty(), request_inputs.has_uploads(), )); diff --git a/src/generators/rust/aioduct/runtime.rs b/src/generators/rust/aioduct/runtime.rs index abcd35e9..230ccc87 100644 --- a/src/generators/rust/aioduct/runtime.rs +++ b/src/generators/rust/aioduct/runtime.rs @@ -3,6 +3,8 @@ use crate::codegen::traits::file_writer::FileInfo; use crate::generators::rust::aioduct::config::{AioductFeatureConfig, AioductTls}; use crate::generators::rust::common::project_files::with_header; +use crate::generators::rust::common::runtime::render_api_call_error; +use sigil_stitch::type_name::TypeName; const CLIENT_RS_TEMPLATE: &str = include_str!("runtime/client.rs.txt"); const ERROR_RS: &str = include_str!("runtime/error.rs.txt"); @@ -14,13 +16,22 @@ const UPLOAD_FILE_RS: &str = include_str!("runtime/upload_file.rs.txt"); pub fn runtime_files( header: &str, aioduct_cfg: &AioductFeatureConfig, + include_api_call_error: bool, include_upload_file: bool, ) -> Vec { let client_rs = render_client_rs(aioduct_cfg); let mut mod_rs = MOD_RS.to_string(); + let mut error_rs = ERROR_RS.to_string(); + if include_api_call_error { + error_rs.push('\n'); + error_rs.push_str( + &render_api_call_error(TypeName::qualified("aioduct", "HeaderMap")) + .expect("aioduct ApiCallError runtime renders"), + ); + } let mut files = vec![ FileInfo::runtime("client.rs".to_string(), with_header(header, &client_rs)), - FileInfo::runtime("error.rs".to_string(), with_header(header, ERROR_RS)), + FileInfo::runtime("error.rs".to_string(), with_header(header, &error_rs)), FileInfo::runtime("auth.rs".to_string(), with_header(header, AUTH_RS)), ]; if include_upload_file { diff --git a/src/generators/rust/common/emit_api.rs b/src/generators/rust/common/emit_api.rs index 252948b4..3a81d202 100644 --- a/src/generators/rust/common/emit_api.rs +++ b/src/generators/rust/common/emit_api.rs @@ -676,6 +676,20 @@ pub fn emit_error_enum(plan: &OpPlan<'_>, response_headers_type: &TypeName) -> C cb.add(" }\n", ()); cb.add("}\n\n", ()); + let operation_id = if plan.op.operation_id.is_empty() { + plan.method_name.as_str() + } else { + plan.op.operation_id.as_str() + }; + cb.add_code(emit_api_call_error_conversion( + &plan.error_type, + operation_id, + &variants, + &unexpected_variant, + &transport_variant, + )); + cb.add_line(); + cb.add_code(emit_rust_error_header_impl( plan, response_headers_type, @@ -731,6 +745,32 @@ pub fn emit_error_enum(plan: &OpPlan<'_>, response_headers_type: &TypeName) -> C cb.build().expect("error enum builds") } +fn emit_api_call_error_conversion( + operation_error_type: &str, + operation_id: &str, + variants: &[(String, String)], + unexpected_variant: &str, + transport_variant: &str, +) -> CodeBlock { + let operation_error_type = TypeName::primitive(operation_error_type); + let api_call_error_type = TypeName::importable("crate::runtime::error", "ApiCallError"); + + sigil_quote!(RustLang { + impl From<$T(operation_error_type.clone())> for $T(api_call_error_type) { + fn from(error: $T(operation_error_type.clone())) -> Self { + match error { + $for((variant, _) in variants) { + $T(operation_error_type.clone())::$N(variant.as_str())(error) => Self::from_api_error($S(operation_id), error), + } + $T(operation_error_type.clone())::$N(unexpected_variant)(error) => Self::from_api_error($S(operation_id), error), + $T(operation_error_type)::$N(transport_variant)(error) => Self::from_runtime_error($S(operation_id), error), + } + } + } + }) + .expect("Rust API call error conversion builds") +} + fn emit_rust_error_header_impl( plan: &OpPlan<'_>, response_headers_type: &TypeName, @@ -1324,3 +1364,44 @@ pub fn error_response_value_expr(er: &ErrorResponse, bytes_var: &str) -> String None => "Ok::<(), Error>(())".to_string(), } } + +#[cfg(test)] +mod tests { + use sigil_stitch::assert_rendered; + + use super::*; + + #[test] + fn api_call_error_conversion_renders_structured_rust() { + let variants = vec![ + ("BadRequest".to_string(), "BadRequestError".to_string()), + ("Conflict".to_string(), "ConflictError".to_string()), + ]; + let conversion = emit_api_call_error_conversion( + "CreateResourceError", + "createResource", + &variants, + "Unexpected", + "Transport", + ); + + assert_rendered!( + Rust::new(), + width = 100, + conversion, + r#"use crate::runtime::error::ApiCallError; + +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::BadRequest(error) => Self::from_api_error("createResource", error), + CreateResourceError::Conflict(error) => Self::from_api_error("createResource", error), + CreateResourceError::Unexpected(error) => Self::from_api_error("createResource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("createResource", error), + } + } +} +"#, + ); + } +} diff --git a/src/generators/rust/common/mod.rs b/src/generators/rust/common/mod.rs index 4f1e9b71..4665fc76 100644 --- a/src/generators/rust/common/mod.rs +++ b/src/generators/rust/common/mod.rs @@ -2,3 +2,4 @@ pub mod config; pub mod emit_api; pub mod emit_models; pub mod project_files; +pub(crate) mod runtime; diff --git a/src/generators/rust/common/runtime.rs b/src/generators/rust/common/runtime.rs new file mode 100644 index 00000000..49b53fd7 --- /dev/null +++ b/src/generators/rust/common/runtime.rs @@ -0,0 +1,262 @@ +//! Shared structured runtime emission for Rust generators. + +use sigil_stitch::code_block::CodeBlock; +use sigil_stitch::error::SigilStitchError; +use sigil_stitch::prelude::sigil_quote; +use sigil_stitch::spec::annotation_spec::AnnotationSpec; +use sigil_stitch::spec::enum_variant_spec::EnumVariantSpec; +use sigil_stitch::spec::field_spec::FieldSpec; +use sigil_stitch::spec::file_spec::FileSpec; +use sigil_stitch::spec::fun_spec::FunSpec; +use sigil_stitch::spec::modifiers::{TypeKind, Visibility}; +use sigil_stitch::spec::parameter_spec::ParameterSpec; +use sigil_stitch::spec::type_spec::TypeSpec; +use sigil_stitch::spec::where_spec::TypeParamSpec; +use sigil_stitch::type_name::TypeName; + +pub(crate) fn render_api_call_error( + response_headers_type: TypeName, +) -> Result { + FileSpec::builder("error.rs") + .add_type(api_call_http_error(response_headers_type.clone())?) + .add_type(api_call_error_kind()?) + .add_type(api_call_error(response_headers_type)?) + .add_code(api_call_error_display()?) + .add_code(api_call_error_source()?) + .build()? + .render(100) +} + +fn api_call_http_error(response_headers_type: TypeName) -> Result { + let api_error_of_t = TypeName::generic( + TypeName::primitive("ApiError"), + vec![TypeName::primitive("T")], + ); + let from_api_error = FunSpec::builder("from_api_error") + .add_type_param(TypeParamSpec::new("T")) + .add_param(ParameterSpec::of("error", api_error_of_t)) + .returns(TypeName::primitive("Self")) + .body(sigil_quote!(RustLang { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + })?) + .build()?; + + TypeSpec::builder("ApiCallHttpError", TypeKind::Struct) + .annotate(AnnotationSpec::new("derive").args(["Debug"])) + .add_field(FieldSpec::builder("status_code", TypeName::primitive("u16")).build()?) + .add_field(FieldSpec::builder("headers", response_headers_type).build()?) + .add_field( + FieldSpec::builder( + "raw_body", + TypeName::generic(TypeName::primitive("Vec"), vec![TypeName::primitive("u8")]), + ) + .build()?, + ) + .add_field( + FieldSpec::builder( + "body_error", + TypeName::optional(TypeName::primitive("Error")), + ) + .build()?, + ) + .add_method(from_api_error) + .build() +} + +fn api_call_error_kind() -> Result { + let http = EnumVariantSpec::builder("Http") + .associated_type(TypeName::primitive("ApiCallHttpError")) + .build()?; + let runtime = EnumVariantSpec::builder("Runtime") + .associated_type(TypeName::primitive("Error")) + .build()?; + + TypeSpec::builder("ApiCallErrorKind", TypeKind::Enum) + .annotate(AnnotationSpec::new("derive").args(["Debug"])) + .add_variant(http) + .add_variant(runtime) + .build() +} + +fn api_call_error(response_headers_type: TypeName) -> Result { + let static_str = operation_id_type(); + let api_error_of_t = TypeName::generic( + TypeName::primitive("ApiError"), + vec![TypeName::primitive("T")], + ); + let optional_status = TypeName::optional(TypeName::primitive("u16")); + let optional_headers = api_call_headers_type(response_headers_type.clone()); + let optional_body = api_call_raw_body_type(); + + let from_api_error = FunSpec::builder("from_api_error") + .visibility(Visibility::PublicCrate) + .add_type_param(TypeParamSpec::new("T")) + .add_param(ParameterSpec::of("operation_id", static_str.clone())) + .add_param(ParameterSpec::of("error", api_error_of_t)) + .returns(TypeName::primitive("Self")) + .body(sigil_quote!(RustLang { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + })?) + .build()?; + let from_runtime_error = FunSpec::builder("from_runtime_error") + .visibility(Visibility::PublicCrate) + .add_param(ParameterSpec::of("operation_id", static_str.clone())) + .add_param(ParameterSpec::of("error", TypeName::primitive("Error"))) + .returns(TypeName::primitive("Self")) + .body(sigil_quote!(RustLang { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + })?) + .build()?; + let operation_id = FunSpec::builder("operation_id") + .visibility(Visibility::Public) + .doc( + "Operation identifier, generated from the HTTP method and path when OpenAPI omits one.", + ) + .add_param(ParameterSpec::of("&self", TypeName::primitive(""))) + .returns(static_str) + .body(sigil_quote!(RustLang { self.operation_id })?) + .build()?; + let status_code = FunSpec::builder("status_code") + .visibility(Visibility::Public) + .doc("HTTP response status, if the operation reached the server and received an error response.") + .add_param(ParameterSpec::of("&self", TypeName::primitive(""))) + .returns(optional_status) + .body( + sigil_quote!(RustLang { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + })?, + ) + .build()?; + let headers = FunSpec::builder("headers") + .visibility(Visibility::Public) + .doc("Native HTTP response headers, if an error response was received.") + .add_param(ParameterSpec::of("&self", TypeName::primitive(""))) + .returns(optional_headers) + .body(sigil_quote!(RustLang { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + })?) + .build()?; + let raw_body = FunSpec::builder("raw_body") + .visibility(Visibility::Public) + .doc("Raw HTTP response body, if an error response was received.") + .add_param(ParameterSpec::of("&self", TypeName::primitive(""))) + .returns(optional_body) + .body(sigil_quote!(RustLang { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + })?) + .build()?; + + TypeSpec::builder("ApiCallError", TypeKind::Struct) + .visibility(Visibility::Public) + .doc("Type-erased error from any generated API operation.") + .annotate(AnnotationSpec::new("derive").args(["Debug"])) + .add_field(FieldSpec::builder("operation_id", operation_id_type()).build()?) + .add_field(FieldSpec::builder("kind", TypeName::primitive("ApiCallErrorKind")).build()?) + .add_method(from_api_error) + .add_method(from_runtime_error) + .add_method(operation_id) + .add_method(status_code) + .add_method(headers) + .add_method(raw_body) + .build() +} + +fn operation_id_type() -> TypeName { + TypeName::reference_with_lifetime(TypeName::primitive("str"), "'static") +} + +fn api_call_headers_type(response_headers_type: TypeName) -> TypeName { + TypeName::optional(TypeName::reference(response_headers_type)) +} + +fn api_call_raw_body_type() -> TypeName { + TypeName::optional(TypeName::slice(TypeName::primitive("u8"))) +} + +fn api_call_error_display() -> Result { + sigil_quote!(RustLang { + impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } + } + }) +} + +fn api_call_error_source() -> Result { + sigil_quote!(RustLang { + impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } + } + }) +} + +#[cfg(test)] +mod tests { + use sigil_stitch::assert_rendered; + use sigil_stitch::lang::rust::Rust; + + use super::*; + + #[test] + fn api_call_error_borrowed_types_render_exactly() { + let operation_id = operation_id_type(); + let accessors = TypeName::tuple(vec![ + api_call_headers_type(TypeName::qualified("reqwest::header", "HeaderMap")), + api_call_raw_body_type(), + ]); + let block = sigil_quote!(RustLang { + fn inspect(operation_id: $T(operation_id)) -> $T(accessors) { + todo!() + } + }) + .expect("ApiCallError borrowed type probe builds"); + + assert_rendered!( + Rust::new(), + width = 100, + block, + r#"fn inspect(operation_id: &'static str) -> (Option<&reqwest::header::HeaderMap>, Option<&[u8]>) { + todo!() +} +"#, + ); + } +} diff --git a/src/generators/rust/reqwest/codegen.rs b/src/generators/rust/reqwest/codegen.rs index 7872c4f0..8eb38cf3 100644 --- a/src/generators/rust/reqwest/codegen.rs +++ b/src/generators/rust/reqwest/codegen.rs @@ -76,7 +76,11 @@ impl RustReqwestCodeGenerator { ); // Runtime (reqwest-specific) - files.extend(runtime_files(&header, request_inputs.has_uploads())); + files.extend(runtime_files( + &header, + !ir.operations.is_empty(), + request_inputs.has_uploads(), + )); // Project files files.push(cargo_toml_file(&crate_name, ir, &self.config)); diff --git a/src/generators/rust/reqwest/runtime.rs b/src/generators/rust/reqwest/runtime.rs index beac9ab2..923357b6 100644 --- a/src/generators/rust/reqwest/runtime.rs +++ b/src/generators/rust/reqwest/runtime.rs @@ -4,6 +4,8 @@ //! trait. These ship as-is in every generated SDK. use crate::codegen::traits::file_writer::FileInfo; +use crate::generators::rust::common::runtime::render_api_call_error; +use sigil_stitch::type_name::TypeName; const CLIENT_RS: &str = include_str!("runtime/client.rs.txt"); const ERROR_RS: &str = include_str!("runtime/error.rs.txt"); @@ -12,11 +14,23 @@ const MOD_RS: &str = include_str!("runtime/mod.rs.txt"); const UPLOAD_FILE_RS: &str = include_str!("runtime/upload_file.rs.txt"); /// Returns runtime files ready to write. -pub fn runtime_files(header: &str, include_upload_file: bool) -> Vec { +pub fn runtime_files( + header: &str, + include_api_call_error: bool, + include_upload_file: bool, +) -> Vec { let mut mod_rs = MOD_RS.to_string(); + let mut error_rs = ERROR_RS.to_string(); + if include_api_call_error { + error_rs.push('\n'); + error_rs.push_str( + &render_api_call_error(TypeName::qualified("reqwest::header", "HeaderMap")) + .expect("reqwest ApiCallError runtime renders"), + ); + } let mut files = vec![ FileInfo::runtime("client.rs".to_string(), with_header(header, CLIENT_RS)), - FileInfo::runtime("error.rs".to_string(), with_header(header, ERROR_RS)), + FileInfo::runtime("error.rs".to_string(), with_header(header, &error_rs)), FileInfo::runtime("auth.rs".to_string(), with_header(header, AUTH_RS)), ]; if include_upload_file { diff --git a/src/generators/rust/ureq/codegen.rs b/src/generators/rust/ureq/codegen.rs index d9ddd6ef..ab015129 100644 --- a/src/generators/rust/ureq/codegen.rs +++ b/src/generators/rust/ureq/codegen.rs @@ -66,7 +66,11 @@ impl RustUreqCodeGenerator { .map_err(|msg| Box::::from(format!("emit_api: {msg}")))?, ); - files.extend(runtime_files(&header, request_inputs.has_uploads())); + files.extend(runtime_files( + &header, + !ir.operations.is_empty(), + request_inputs.has_uploads(), + )); files.push(cargo_toml_file(&crate_name, &ir.info, &self.config)); files.push(project_files::lib_rs_file(&header)); diff --git a/src/generators/rust/ureq/runtime.rs b/src/generators/rust/ureq/runtime.rs index 89e8d02e..0349880a 100644 --- a/src/generators/rust/ureq/runtime.rs +++ b/src/generators/rust/ureq/runtime.rs @@ -2,6 +2,8 @@ use crate::codegen::traits::file_writer::FileInfo; use crate::generators::rust::common::project_files::with_header; +use crate::generators::rust::common::runtime::render_api_call_error; +use sigil_stitch::type_name::TypeName; const CLIENT_RS: &str = include_str!("runtime/client.rs.txt"); const ERROR_RS: &str = include_str!("runtime/error.rs.txt"); @@ -10,11 +12,23 @@ const MOD_RS: &str = include_str!("runtime/mod.rs.txt"); const UPLOAD_FILE_RS: &str = include_str!("runtime/upload_file.rs.txt"); /// Returns runtime files ready to write. -pub fn runtime_files(header: &str, include_upload_file: bool) -> Vec { +pub fn runtime_files( + header: &str, + include_api_call_error: bool, + include_upload_file: bool, +) -> Vec { let mut mod_rs = MOD_RS.to_string(); + let mut error_rs = ERROR_RS.to_string(); + if include_api_call_error { + error_rs.push('\n'); + error_rs.push_str( + &render_api_call_error(TypeName::qualified("ureq::http", "HeaderMap")) + .expect("ureq ApiCallError runtime renders"), + ); + } let mut files = vec![ FileInfo::runtime("client.rs".to_string(), with_header(header, CLIENT_RS)), - FileInfo::runtime("error.rs".to_string(), with_header(header, ERROR_RS)), + FileInfo::runtime("error.rs".to_string(), with_header(header, &error_rs)), FileInfo::runtime("auth.rs".to_string(), with_header(header, AUTH_RS)), ]; if include_upload_file { diff --git a/tests/golden/rust/rust-aioduct/additional-properties/src/apis/additional_properties.rs.golden b/tests/golden/rust/rust-aioduct/additional-properties/src/apis/additional_properties.rs.golden index dbbf596f..a458832d 100644 --- a/tests/golden/rust/rust-aioduct/additional-properties/src/apis/additional_properties.rs.golden +++ b/tests/golden/rust/rust-aioduct/additional-properties/src/apis/additional_properties.rs.golden @@ -5,7 +5,7 @@ // API demonstrating OpenAPI additionalProperties with multiple levels of structs (RootLevel -> MiddleLevel -> LeafValue), each with HashMap fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "additional-properties" tag. pub struct AdditionalPropertiesApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -145,6 +145,16 @@ impl From for PostLeafError { } } +impl From for ApiCallError { + fn from(error: PostLeafError) -> Self { + match error { + PostLeafError::BadRequest(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Unexpected(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Transport(error) => Self::from_runtime_error("post_leaf", error), + } + } +} + impl std::fmt::Display for PostLeafError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -186,6 +196,16 @@ impl From for PostMiddleError { } } +impl From for ApiCallError { + fn from(error: PostMiddleError) -> Self { + match error { + PostMiddleError::BadRequest(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Unexpected(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Transport(error) => Self::from_runtime_error("post_middle", error), + } + } +} + impl std::fmt::Display for PostMiddleError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -227,6 +247,16 @@ impl From for PostRootError { } } +impl From for ApiCallError { + fn from(error: PostRootError) -> Self { + match error { + PostRootError::BadRequest(error) => Self::from_api_error("post_root", error), + PostRootError::Unexpected(error) => Self::from_api_error("post_root", error), + PostRootError::Transport(error) => Self::from_runtime_error("post_root", error), + } + } +} + impl std::fmt::Display for PostRootError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/additional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/additional-properties/src/runtime/error.rs.golden index 405f89f7..2efbdf51 100644 --- a/tests/golden/rust/rust-aioduct/additional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/additional-properties/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/apis/transfer.rs.golden b/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/apis/transfer.rs.golden index 317f71f5..127e4705 100644 --- a/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart upload and octet-stream download. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -115,6 +115,16 @@ impl From for DownloadAssetError { } } +impl From for ApiCallError { + fn from(error: DownloadAssetError) -> Self { + match error { + DownloadAssetError::NotFound(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Unexpected(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Transport(error) => Self::from_runtime_error("download_asset", error), + } + } +} + impl std::fmt::Display for DownloadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -156,6 +166,16 @@ impl From for UploadAssetError { } } +impl From for ApiCallError { + fn from(error: UploadAssetError) -> Self { + match error { + UploadAssetError::BadRequest(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Unexpected(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Transport(error) => Self::from_runtime_error("upload_asset", error), + } + } +} + impl std::fmt::Display for UploadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/runtime/error.rs.golden index 8029c74b..9db23476 100644 --- a/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/binary-transfer-media-types/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/apis/default.rs.golden index e9380f4d..2a63b3e2 100644 --- a/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Comprehensive test for all OpenAPI v3.1.2 schema types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/runtime/error.rs.golden index e480d151..24133ff0 100644 --- a/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/comprehensive-schemas/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/apis/test_resource.rs.golden b/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/apis/test_resource.rs.golden index 7b0cee1a..cbc1a421 100644 --- a/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/apis/test_resource.rs.golden +++ b/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/apis/test_resource.rs.golden @@ -5,7 +5,7 @@ // Test fixture for DELETE operations with JSON response schemas and type alias request bodies use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-resource" tag. pub struct TestResourceApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -110,6 +110,15 @@ impl From for CreateTestResourceError { } } +impl From for ApiCallError { + fn from(error: CreateTestResourceError) -> Self { + match error { + CreateTestResourceError::Unexpected(error) => Self::from_api_error("create_test_resource", error), + CreateTestResourceError::Transport(error) => Self::from_runtime_error("create_test_resource", error), + } + } +} + impl std::fmt::Display for CreateTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -151,6 +160,17 @@ impl From for DeleteTestResourceError { } } +impl From for ApiCallError { + fn from(error: DeleteTestResourceError) -> Self { + match error { + DeleteTestResourceError::ClientError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::ServerError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Unexpected(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Transport(error) => Self::from_runtime_error("delete_test_resource", error), + } + } +} + impl std::fmt::Display for DeleteTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/runtime/error.rs.golden index 1592ccbc..d34013bc 100644 --- a/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/delete-with-response-schema/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/items.rs.golden b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/items.rs.golden index 2dfefe06..01448d23 100644 --- a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -88,6 +88,16 @@ impl From for CreateItemWithBodyConflictError { } } +impl From for ApiCallError { + fn from(error: CreateItemWithBodyConflictError) -> Self { + match error { + CreateItemWithBodyConflictError::BadRequest(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Unexpected(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Transport(error) => Self::from_runtime_error("create_item_with_body_conflict", error), + } + } +} + impl std::fmt::Display for CreateItemWithBodyConflictError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/users.rs.golden b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/users.rs.golden index 62ff9772..326d1fc1 100644 --- a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/users.rs.golden +++ b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/apis/users.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "users" tag. pub struct UsersApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -95,6 +95,17 @@ impl From for GetUserByIdDuplicateError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdDuplicateError) -> Self { + match error { + GetUserByIdDuplicateError::BadRequest(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::NotFound(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Unexpected(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Transport(error) => Self::from_runtime_error("get_user_by_id_duplicate", error), + } + } +} + impl std::fmt::Display for GetUserByIdDuplicateError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/runtime/error.rs.golden index 2d83f6e2..9d75448e 100644 --- a/tests/golden/rust/rust-aioduct/duplicate-param-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/duplicate-param-names/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/enum-repr/src/apis/enum_repr.rs.golden b/tests/golden/rust/rust-aioduct/enum-repr/src/apis/enum_repr.rs.golden index ef5bb0df..71e8e035 100644 --- a/tests/golden/rust/rust-aioduct/enum-repr/src/apis/enum_repr.rs.golden +++ b/tests/golden/rust/rust-aioduct/enum-repr/src/apis/enum_repr.rs.golden @@ -5,7 +5,7 @@ // This API demonstrates all 4 kinds of enum representation types: Externally Tagged, Internally Tagged, Adjacently Tagged, and Untagged use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "enum-repr" tag. pub struct EnumReprApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -213,6 +213,16 @@ impl From for HandleAdjacentlyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleAdjacentlyTaggedError) -> Self { + match error { + HandleAdjacentlyTaggedError::BadRequest(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Unexpected(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Transport(error) => Self::from_runtime_error("handle_adjacently_tagged", error), + } + } +} + impl std::fmt::Display for HandleAdjacentlyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -254,6 +264,16 @@ impl From for HandleExternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleExternallyTaggedError) -> Self { + match error { + HandleExternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_externally_tagged", error), + } + } +} + impl std::fmt::Display for HandleExternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -295,6 +315,16 @@ impl From for HandleInternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleInternallyTaggedError) -> Self { + match error { + HandleInternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_internally_tagged", error), + } + } +} + impl std::fmt::Display for HandleInternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -336,6 +366,16 @@ impl From for HandleMixedError { } } +impl From for ApiCallError { + fn from(error: HandleMixedError) -> Self { + match error { + HandleMixedError::BadRequest(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Unexpected(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Transport(error) => Self::from_runtime_error("handle_mixed", error), + } + } +} + impl std::fmt::Display for HandleMixedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -377,6 +417,16 @@ impl From for HandleUntaggedError { } } +impl From for ApiCallError { + fn from(error: HandleUntaggedError) -> Self { + match error { + HandleUntaggedError::BadRequest(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Unexpected(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Transport(error) => Self::from_runtime_error("handle_untagged", error), + } + } +} + impl std::fmt::Display for HandleUntaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/enum-repr/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/enum-repr/src/runtime/error.rs.golden index 88cf3cba..c3f68c65 100644 --- a/tests/golden/rust/rust-aioduct/enum-repr/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/enum-repr/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/media-type-selection/src/apis/media.rs.golden b/tests/golden/rust/rust-aioduct/media-type-selection/src/apis/media.rs.golden index c296c18f..eaa4a84f 100644 --- a/tests/golden/rust/rust-aioduct/media-type-selection/src/apis/media.rs.golden +++ b/tests/golden/rust/rust-aioduct/media-type-selection/src/apis/media.rs.golden @@ -5,7 +5,7 @@ // Covers normalized media-type selection for requests and responses. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "media" tag. pub struct MediaApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -208,6 +208,15 @@ impl From for SendJsonPreferredError { } } +impl From for ApiCallError { + fn from(error: SendJsonPreferredError) -> Self { + match error { + SendJsonPreferredError::Unexpected(error) => Self::from_api_error("send_json_preferred", error), + SendJsonPreferredError::Transport(error) => Self::from_runtime_error("send_json_preferred", error), + } + } +} + impl std::fmt::Display for SendJsonPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -246,6 +255,15 @@ impl From for SendParameterizedMultipartError { } } +impl From for ApiCallError { + fn from(error: SendParameterizedMultipartError) -> Self { + match error { + SendParameterizedMultipartError::Unexpected(error) => Self::from_api_error("send_parameterized_multipart", error), + SendParameterizedMultipartError::Transport(error) => Self::from_runtime_error("send_parameterized_multipart", error), + } + } +} + impl std::fmt::Display for SendParameterizedMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -284,6 +302,15 @@ impl From for SendVendorJsonError { } } +impl From for ApiCallError { + fn from(error: SendVendorJsonError) -> Self { + match error { + SendVendorJsonError::Unexpected(error) => Self::from_api_error("send_vendor_json", error), + SendVendorJsonError::Transport(error) => Self::from_runtime_error("send_vendor_json", error), + } + } +} + impl std::fmt::Display for SendVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -323,6 +350,15 @@ impl From for GetOctetPreferredError { } } +impl From for ApiCallError { + fn from(error: GetOctetPreferredError) -> Self { + match error { + GetOctetPreferredError::Unexpected(error) => Self::from_api_error("get_octet_preferred", error), + GetOctetPreferredError::Transport(error) => Self::from_runtime_error("get_octet_preferred", error), + } + } +} + impl std::fmt::Display for GetOctetPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -362,6 +398,15 @@ impl From for GetTextPreferredError { } } +impl From for ApiCallError { + fn from(error: GetTextPreferredError) -> Self { + match error { + GetTextPreferredError::Unexpected(error) => Self::from_api_error("get_text_preferred", error), + GetTextPreferredError::Transport(error) => Self::from_runtime_error("get_text_preferred", error), + } + } +} + impl std::fmt::Display for GetTextPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -401,6 +446,15 @@ impl From for GetVendorJsonError { } } +impl From for ApiCallError { + fn from(error: GetVendorJsonError) -> Self { + match error { + GetVendorJsonError::Unexpected(error) => Self::from_api_error("get_vendor_json", error), + GetVendorJsonError::Transport(error) => Self::from_runtime_error("get_vendor_json", error), + } + } +} + impl std::fmt::Display for GetVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/media-type-selection/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/media-type-selection/src/runtime/error.rs.golden index 43f5c77d..9e412f08 100644 --- a/tests/golden/rust/rust-aioduct/media-type-selection/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/media-type-selection/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/minimal/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/minimal/src/apis/default.rs.golden index 59789e2d..40909803 100644 --- a/tests/golden/rust/rust-aioduct/minimal/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/minimal/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Minimal API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -64,6 +64,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("getTest", error), + GetTestError::Transport(error) => Self::from_runtime_error("getTest", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/minimal/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/minimal/src/runtime/error.rs.golden index 059f4514..28fea5aa 100644 --- a/tests/golden/rust/rust-aioduct/minimal/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/minimal/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/apis/default.rs.golden index 9ee252c2..940ccd35 100644 --- a/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // and primitive type alias suppression. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -111,6 +111,15 @@ impl From for AcceptNodeError { } } +impl From for ApiCallError { + fn from(error: AcceptNodeError) -> Self { + match error { + AcceptNodeError::Unexpected(error) => Self::from_api_error("acceptNode", error), + AcceptNodeError::Transport(error) => Self::from_runtime_error("acceptNode", error), + } + } +} + impl std::fmt::Display for AcceptNodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -150,6 +159,15 @@ impl From for GetNodeNameError { } } +impl From for ApiCallError { + fn from(error: GetNodeNameError) -> Self { + match error { + GetNodeNameError::Unexpected(error) => Self::from_api_error("getNodeName", error), + GetNodeNameError::Transport(error) => Self::from_runtime_error("getNodeName", error), + } + } +} + impl std::fmt::Display for GetNodeNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden index 9953be99..052a83b9 100644 --- a/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden @@ -106,3 +106,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/apis/multipart.rs.golden b/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/apis/multipart.rs.golden index b8cf6a13..8b907eeb 100644 --- a/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers optional multipart bodies, optional parts, and text-only multipart fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -115,6 +115,15 @@ impl From for SendOptionalPartsError { } } +impl From for ApiCallError { + fn from(error: SendOptionalPartsError) -> Self { + match error { + SendOptionalPartsError::Unexpected(error) => Self::from_api_error("send_optional_parts", error), + SendOptionalPartsError::Transport(error) => Self::from_runtime_error("send_optional_parts", error), + } + } +} + impl std::fmt::Display for SendOptionalPartsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -153,6 +162,15 @@ impl From for SendTextFieldsError { } } +impl From for ApiCallError { + fn from(error: SendTextFieldsError) -> Self { + match error { + SendTextFieldsError::Unexpected(error) => Self::from_api_error("send_text_fields", error), + SendTextFieldsError::Transport(error) => Self::from_runtime_error("send_text_fields", error), + } + } +} + impl std::fmt::Display for SendTextFieldsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/runtime/error.rs.golden index 10976e38..da0d971c 100644 --- a/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-edge-cases/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/apis/transfer.rs.golden b/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/apis/transfer.rs.golden index 284fe754..3304122a 100644 --- a/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers explicit multipart part content types. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for UploadEncodedAssetError { } } +impl From for ApiCallError { + fn from(error: UploadEncodedAssetError) -> Self { + match error { + UploadEncodedAssetError::Unexpected(error) => Self::from_api_error("upload_encoded_asset", error), + UploadEncodedAssetError::Transport(error) => Self::from_runtime_error("upload_encoded_asset", error), + } + } +} + impl std::fmt::Display for UploadEncodedAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/runtime/error.rs.golden index 133799b6..b5b14ff3 100644 --- a/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-explicit-encoding/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/apis/multipart.rs.golden b/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/apis/multipart.rs.golden index ede4860f..c41c7bca 100644 --- a/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers multipart object parts whose wire names differ from ergonomic names. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -70,6 +70,15 @@ impl From for SendNestedObjectPartError { } } +impl From for ApiCallError { + fn from(error: SendNestedObjectPartError) -> Self { + match error { + SendNestedObjectPartError::Unexpected(error) => Self::from_api_error("send_nested_object_part", error), + SendNestedObjectPartError::Transport(error) => Self::from_runtime_error("send_nested_object_part", error), + } + } +} + impl std::fmt::Display for SendNestedObjectPartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/runtime/error.rs.golden index e00e59c7..6240d343 100644 --- a/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-nested-object-parts/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/apis/transfer.rs.golden b/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/apis/transfer.rs.golden index dd2e2ebe..7dd7eaef 100644 --- a/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart request bodies that are not object-shaped. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -51,6 +51,15 @@ impl From for UploadRawMultipartError { } } +impl From for ApiCallError { + fn from(error: UploadRawMultipartError) -> Self { + match error { + UploadRawMultipartError::Unexpected(error) => Self::from_api_error("upload_raw_multipart", error), + UploadRawMultipartError::Transport(error) => Self::from_runtime_error("upload_raw_multipart", error), + } + } +} + impl std::fmt::Display for UploadRawMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/runtime/error.rs.golden index cfb5f919..f582a28c 100644 --- a/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multipart-unsupported-schema/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/apis/test_api.rs.golden b/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/apis/test_api.rs.golden index 5d535cd7..e18d774c 100644 --- a/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/apis/test_api.rs.golden +++ b/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/apis/test_api.rs.golden @@ -5,7 +5,7 @@ // Test API with multiple operations having similar request body schema names use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-api" tag. pub struct TestApiApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -102,6 +102,15 @@ impl From for CreateTypeAError { } } +impl From for ApiCallError { + fn from(error: CreateTypeAError) -> Self { + match error { + CreateTypeAError::Unexpected(error) => Self::from_api_error("create_type_a", error), + CreateTypeAError::Transport(error) => Self::from_runtime_error("create_type_a", error), + } + } +} + impl std::fmt::Display for CreateTypeAError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -141,6 +150,15 @@ impl From for CreateTypeBError { } } +impl From for ApiCallError { + fn from(error: CreateTypeBError) -> Self { + match error { + CreateTypeBError::Unexpected(error) => Self::from_api_error("create_type_b", error), + CreateTypeBError::Transport(error) => Self::from_runtime_error("create_type_b", error), + } + } +} + impl std::fmt::Display for CreateTypeBError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/runtime/error.rs.golden index 49a50093..e4a7b4a3 100644 --- a/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/multiple-similar-request-schemas/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/naming-conventions/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/naming-conventions/src/apis/default.rs.golden index 53924270..d11e2a6b 100644 --- a/tests/golden/rust/rust-aioduct/naming-conventions/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/naming-conventions/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture to verify language property naming conventions use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -146,6 +146,15 @@ impl From for GetWithQueryParamsError { } } +impl From for ApiCallError { + fn from(error: GetWithQueryParamsError) -> Self { + match error { + GetWithQueryParamsError::Unexpected(error) => Self::from_api_error("get_with_query_params", error), + GetWithQueryParamsError::Transport(error) => Self::from_runtime_error("get_with_query_params", error), + } + } +} + impl std::fmt::Display for GetWithQueryParamsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -185,6 +194,15 @@ impl From for TestNamingConventionsError { } } +impl From for ApiCallError { + fn from(error: TestNamingConventionsError) -> Self { + match error { + TestNamingConventionsError::Unexpected(error) => Self::from_api_error("test_naming_conventions", error), + TestNamingConventionsError::Transport(error) => Self::from_runtime_error("test_naming_conventions", error), + } + } +} + impl std::fmt::Display for TestNamingConventionsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/naming-conventions/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/naming-conventions/src/runtime/error.rs.golden index b46e3bd2..4d4616f4 100644 --- a/tests/golden/rust/rust-aioduct/naming-conventions/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/naming-conventions/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/optional-request-bodies/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/optional-request-bodies/src/apis/default.rs.golden index e47e9d3b..433160df 100644 --- a/tests/golden/rust/rust-aioduct/optional-request-bodies/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/optional-request-bodies/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers optional non-multipart request bodies. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -153,6 +153,15 @@ impl From for SendOptionalBinaryError { } } +impl From for ApiCallError { + fn from(error: SendOptionalBinaryError) -> Self { + match error { + SendOptionalBinaryError::Unexpected(error) => Self::from_api_error("send_optional_binary", error), + SendOptionalBinaryError::Transport(error) => Self::from_runtime_error("send_optional_binary", error), + } + } +} + impl std::fmt::Display for SendOptionalBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -191,6 +200,15 @@ impl From for SendOptionalJsonError { } } +impl From for ApiCallError { + fn from(error: SendOptionalJsonError) -> Self { + match error { + SendOptionalJsonError::Unexpected(error) => Self::from_api_error("send_optional_json", error), + SendOptionalJsonError::Transport(error) => Self::from_runtime_error("send_optional_json", error), + } + } +} + impl std::fmt::Display for SendOptionalJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -229,6 +247,15 @@ impl From for SendOptionalTextError { } } +impl From for ApiCallError { + fn from(error: SendOptionalTextError) -> Self { + match error { + SendOptionalTextError::Unexpected(error) => Self::from_api_error("send_optional_text", error), + SendOptionalTextError::Transport(error) => Self::from_runtime_error("send_optional_text", error), + } + } +} + impl std::fmt::Display for SendOptionalTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -267,6 +294,15 @@ impl From for SendRequiredJsonError { } } +impl From for ApiCallError { + fn from(error: SendRequiredJsonError) -> Self { + match error { + SendRequiredJsonError::Unexpected(error) => Self::from_api_error("send_required_json", error), + SendRequiredJsonError::Transport(error) => Self::from_runtime_error("send_required_json", error), + } + } +} + impl std::fmt::Display for SendRequiredJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/optional-request-bodies/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/optional-request-bodies/src/runtime/error.rs.golden index ecf91104..79bc5e46 100644 --- a/tests/golden/rust/rust-aioduct/optional-request-bodies/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/optional-request-bodies/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/petstore/src/apis/pet.rs.golden b/tests/golden/rust/rust-aioduct/petstore/src/apis/pet.rs.golden index c5ca9035..8e7543b6 100644 --- a/tests/golden/rust/rust-aioduct/petstore/src/apis/pet.rs.golden +++ b/tests/golden/rust/rust-aioduct/petstore/src/apis/pet.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "pet" tag. pub struct PetApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -357,6 +357,18 @@ impl From for UpdatePetError { } } +impl From for ApiCallError { + fn from(error: UpdatePetError) -> Self { + match error { + UpdatePetError::BadRequest(error) => Self::from_api_error("update_pet", error), + UpdatePetError::NotFound(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Validation(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Unexpected(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Transport(error) => Self::from_runtime_error("update_pet", error), + } + } +} + impl std::fmt::Display for UpdatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -401,6 +413,17 @@ impl From for AddPetError { } } +impl From for ApiCallError { + fn from(error: AddPetError) -> Self { + match error { + AddPetError::BadRequest(error) => Self::from_api_error("add_pet", error), + AddPetError::Validation(error) => Self::from_api_error("add_pet", error), + AddPetError::Unexpected(error) => Self::from_api_error("add_pet", error), + AddPetError::Transport(error) => Self::from_runtime_error("add_pet", error), + } + } +} + impl std::fmt::Display for AddPetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -443,6 +466,16 @@ impl From for FindPetsByStatusError { } } +impl From for ApiCallError { + fn from(error: FindPetsByStatusError) -> Self { + match error { + FindPetsByStatusError::BadRequest(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Unexpected(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Transport(error) => Self::from_runtime_error("find_pets_by_status", error), + } + } +} + impl std::fmt::Display for FindPetsByStatusError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -484,6 +517,16 @@ impl From for FindPetsByTagsError { } } +impl From for ApiCallError { + fn from(error: FindPetsByTagsError) -> Self { + match error { + FindPetsByTagsError::BadRequest(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Unexpected(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Transport(error) => Self::from_runtime_error("find_pets_by_tags", error), + } + } +} + impl std::fmt::Display for FindPetsByTagsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -526,6 +569,17 @@ impl From for GetPetByIdError { } } +impl From for ApiCallError { + fn from(error: GetPetByIdError) -> Self { + match error { + GetPetByIdError::BadRequest(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::NotFound(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Unexpected(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Transport(error) => Self::from_runtime_error("get_pet_by_id", error), + } + } +} + impl std::fmt::Display for GetPetByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -568,6 +622,16 @@ impl From for UpdatePetWithFormError { } } +impl From for ApiCallError { + fn from(error: UpdatePetWithFormError) -> Self { + match error { + UpdatePetWithFormError::BadRequest(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Unexpected(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Transport(error) => Self::from_runtime_error("update_pet_with_form", error), + } + } +} + impl std::fmt::Display for UpdatePetWithFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -608,6 +672,16 @@ impl From for DeletePetError { } } +impl From for ApiCallError { + fn from(error: DeletePetError) -> Self { + match error { + DeletePetError::BadRequest(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Unexpected(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Transport(error) => Self::from_runtime_error("delete_pet", error), + } + } +} + impl std::fmt::Display for DeletePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -648,6 +722,15 @@ impl From for UploadFileError { } } +impl From for ApiCallError { + fn from(error: UploadFileError) -> Self { + match error { + UploadFileError::Unexpected(error) => Self::from_api_error("upload_file", error), + UploadFileError::Transport(error) => Self::from_runtime_error("upload_file", error), + } + } +} + impl std::fmt::Display for UploadFileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/petstore/src/apis/store.rs.golden b/tests/golden/rust/rust-aioduct/petstore/src/apis/store.rs.golden index 0acc22f7..61ec0b69 100644 --- a/tests/golden/rust/rust-aioduct/petstore/src/apis/store.rs.golden +++ b/tests/golden/rust/rust-aioduct/petstore/src/apis/store.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "store" tag. pub struct StoreApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -176,6 +176,15 @@ impl From for GetInventoryError { } } +impl From for ApiCallError { + fn from(error: GetInventoryError) -> Self { + match error { + GetInventoryError::Unexpected(error) => Self::from_api_error("get_inventory", error), + GetInventoryError::Transport(error) => Self::from_runtime_error("get_inventory", error), + } + } +} + impl std::fmt::Display for GetInventoryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -216,6 +225,16 @@ impl From for PlaceOrderError { } } +impl From for ApiCallError { + fn from(error: PlaceOrderError) -> Self { + match error { + PlaceOrderError::BadRequest(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Unexpected(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Transport(error) => Self::from_runtime_error("place_order", error), + } + } +} + impl std::fmt::Display for PlaceOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -258,6 +277,17 @@ impl From for GetOrderByIdError { } } +impl From for ApiCallError { + fn from(error: GetOrderByIdError) -> Self { + match error { + GetOrderByIdError::BadRequest(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::NotFound(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Unexpected(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Transport(error) => Self::from_runtime_error("get_order_by_id", error), + } + } +} + impl std::fmt::Display for GetOrderByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -300,6 +330,17 @@ impl From for DeleteOrderError { } } +impl From for ApiCallError { + fn from(error: DeleteOrderError) -> Self { + match error { + DeleteOrderError::BadRequest(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::NotFound(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Unexpected(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Transport(error) => Self::from_runtime_error("delete_order", error), + } + } +} + impl std::fmt::Display for DeleteOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/petstore/src/apis/user.rs.golden b/tests/golden/rust/rust-aioduct/petstore/src/apis/user.rs.golden index c55602fa..f46ef3fe 100644 --- a/tests/golden/rust/rust-aioduct/petstore/src/apis/user.rs.golden +++ b/tests/golden/rust/rust-aioduct/petstore/src/apis/user.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "user" tag. pub struct UserApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -275,6 +275,15 @@ impl From for CreateUserError { } } +impl From for ApiCallError { + fn from(error: CreateUserError) -> Self { + match error { + CreateUserError::Unexpected(error) => Self::from_api_error("create_user", error), + CreateUserError::Transport(error) => Self::from_runtime_error("create_user", error), + } + } +} + impl std::fmt::Display for CreateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -314,6 +323,15 @@ impl From for CreateUsersWithListInputError { } } +impl From for ApiCallError { + fn from(error: CreateUsersWithListInputError) -> Self { + match error { + CreateUsersWithListInputError::Unexpected(error) => Self::from_api_error("create_users_with_list_input", error), + CreateUsersWithListInputError::Transport(error) => Self::from_runtime_error("create_users_with_list_input", error), + } + } +} + impl std::fmt::Display for CreateUsersWithListInputError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -353,6 +371,16 @@ impl From for LoginUserError { } } +impl From for ApiCallError { + fn from(error: LoginUserError) -> Self { + match error { + LoginUserError::BadRequest(error) => Self::from_api_error("login_user", error), + LoginUserError::Unexpected(error) => Self::from_api_error("login_user", error), + LoginUserError::Transport(error) => Self::from_runtime_error("login_user", error), + } + } +} + impl std::fmt::Display for LoginUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -392,6 +420,15 @@ impl From for LogoutUserError { } } +impl From for ApiCallError { + fn from(error: LogoutUserError) -> Self { + match error { + LogoutUserError::Unexpected(error) => Self::from_api_error("logout_user", error), + LogoutUserError::Transport(error) => Self::from_runtime_error("logout_user", error), + } + } +} + impl std::fmt::Display for LogoutUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -433,6 +470,17 @@ impl From for GetUserByNameError { } } +impl From for ApiCallError { + fn from(error: GetUserByNameError) -> Self { + match error { + GetUserByNameError::BadRequest(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::NotFound(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Unexpected(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Transport(error) => Self::from_runtime_error("get_user_by_name", error), + } + } +} + impl std::fmt::Display for GetUserByNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -475,6 +523,17 @@ impl From for UpdateUserError { } } +impl From for ApiCallError { + fn from(error: UpdateUserError) -> Self { + match error { + UpdateUserError::BadRequest(error) => Self::from_api_error("update_user", error), + UpdateUserError::NotFound(error) => Self::from_api_error("update_user", error), + UpdateUserError::Unexpected(error) => Self::from_api_error("update_user", error), + UpdateUserError::Transport(error) => Self::from_runtime_error("update_user", error), + } + } +} + impl std::fmt::Display for UpdateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -517,6 +576,17 @@ impl From for DeleteUserError { } } +impl From for ApiCallError { + fn from(error: DeleteUserError) -> Self { + match error { + DeleteUserError::BadRequest(error) => Self::from_api_error("delete_user", error), + DeleteUserError::NotFound(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Unexpected(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Transport(error) => Self::from_runtime_error("delete_user", error), + } + } +} + impl std::fmt::Display for DeleteUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/petstore/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/petstore/src/runtime/error.rs.golden index 5cc4dc9f..d7de47a1 100644 --- a/tests/golden/rust/rust-aioduct/petstore/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/petstore/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/query-param-enum/src/apis/items.rs.golden b/tests/golden/rust/rust-aioduct/query-param-enum/src/apis/items.rs.golden index abcfee3c..6323f474 100644 --- a/tests/golden/rust/rust-aioduct/query-param-enum/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-aioduct/query-param-enum/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test case for query parameters that reference enum schemas use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -84,6 +84,16 @@ impl From for GetItemsError { } } +impl From for ApiCallError { + fn from(error: GetItemsError) -> Self { + match error { + GetItemsError::BadRequest(error) => Self::from_api_error("get_items", error), + GetItemsError::Unexpected(error) => Self::from_api_error("get_items", error), + GetItemsError::Transport(error) => Self::from_runtime_error("get_items", error), + } + } +} + impl std::fmt::Display for GetItemsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/query-param-enum/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/query-param-enum/src/runtime/error.rs.golden index c8877d75..243cef68 100644 --- a/tests/golden/rust/rust-aioduct/query-param-enum/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/query-param-enum/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/apis/default.rs.golden index 520b9a3e..19f522fa 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with all optional properties including arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/runtime/error.rs.golden index d75b38be..ef9d914d 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-all-optional-properties/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/apis/default.rs.golden index e202c690..61a61f4c 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects with snake_case to camelCase conversion (main case from user issue) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden index 711cca33..3c6effae 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/apis/default.rs.golden index 64a734b6..83d54b31 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of referenced model types with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden index db3a36f7..515477ab 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/apis/default.rs.golden index 817b0474..7a21951c 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects that contain a reference property use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/runtime/error.rs.golden index 58b7e050..10b22d04 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-array-with-reference-property/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/apis/default.rs.golden index 5315d457..0d101f68 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects where each object has nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/runtime/error.rs.golden index 341961ef..7ceb584e 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-complex-array-structure/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/apis/default.rs.golden index 0e1873e6..d50c14a9 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for three levels of nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden index b33481ce..b8220126 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/apis/default.rs.golden index f98af1a3..8c2ca082 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for empty array handling use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/runtime/error.rs.golden index 639adef4..5221954a 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-empty-array/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/apis/default.rs.golden index 8eb0f000..38fb9557 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline object containing an array of inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/runtime/error.rs.golden index 97b4ed2d..337006c1 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-inline-object-with-array/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/apis/default.rs.golden index 66ec6374..77ad3411 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline objects (not arrays) with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/runtime/error.rs.golden index ad008e80..d7995c73 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-inline-object/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/apis/default.rs.golden index 651a74b9..9c408038 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with mix of simple types, arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/runtime/error.rs.golden index 1ef13221..d7a8b747 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-mixed-property-types/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/apis/default.rs.golden index de53794a..fac6529a 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for nested object reference with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/runtime/error.rs.golden index 5f619d57..b5f8dd3e 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-nested-object-reference/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden index 6e5d06f1..ef9ab644 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of inline objects with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden index aff8e1fc..e2b493cb 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden index 5c129c28..5b785306 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of referenced model types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden index b5e13631..8d677597 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/apis/default.rs.golden index 3abad03c..b63728b2 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/runtime/error.rs.golden index 228b1b19..1d857ca3 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-inline-object/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden index 46a6aa08..9bd8a84d 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional nested object reference use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden index f06ec957..c9a66bc7 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/apis/default.rs.golden index 5f506667..1669fb6a 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for arrays with primitive items (should not be affected by recursive parsing) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/runtime/error.rs.golden index c38b52b1..fc4135a5 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-primitive-array/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/apis/default.rs.golden index 9551bf8c..12347d6f 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Self-Referential Object Test — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -69,6 +69,15 @@ impl From for GetFooError { } } +impl From for ApiCallError { + fn from(error: GetFooError) -> Self { + match error { + GetFooError::Unexpected(error) => Self::from_api_error("getFoo", error), + GetFooError::Transport(error) => Self::from_runtime_error("getFoo", error), + } + } +} + impl std::fmt::Display for GetFooError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/runtime/error.rs.golden index a83f70d7..4c06534c 100644 --- a/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/recursive-json-self-referential-object/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/request-body-content-types/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/request-body-content-types/src/apis/default.rs.golden index 925a2019..72f4cf9b 100644 --- a/tests/golden/rust/rust-aioduct/request-body-content-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/request-body-content-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers non-JSON request body media types to pin Content-Type emission. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -257,6 +257,15 @@ impl From for PostBinaryError { } } +impl From for ApiCallError { + fn from(error: PostBinaryError) -> Self { + match error { + PostBinaryError::Unexpected(error) => Self::from_api_error("postBinary", error), + PostBinaryError::Transport(error) => Self::from_runtime_error("postBinary", error), + } + } +} + impl std::fmt::Display for PostBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -295,6 +304,15 @@ impl From for PostFormError { } } +impl From for ApiCallError { + fn from(error: PostFormError) -> Self { + match error { + PostFormError::Unexpected(error) => Self::from_api_error("postForm", error), + PostFormError::Transport(error) => Self::from_runtime_error("postForm", error), + } + } +} + impl std::fmt::Display for PostFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -333,6 +351,15 @@ impl From for PostJsonError { } } +impl From for ApiCallError { + fn from(error: PostJsonError) -> Self { + match error { + PostJsonError::Unexpected(error) => Self::from_api_error("postJson", error), + PostJsonError::Transport(error) => Self::from_runtime_error("postJson", error), + } + } +} + impl std::fmt::Display for PostJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -371,6 +398,15 @@ impl From for PostJsonOrXmlError { } } +impl From for ApiCallError { + fn from(error: PostJsonOrXmlError) -> Self { + match error { + PostJsonOrXmlError::Unexpected(error) => Self::from_api_error("postJsonOrXml", error), + PostJsonOrXmlError::Transport(error) => Self::from_runtime_error("postJsonOrXml", error), + } + } +} + impl std::fmt::Display for PostJsonOrXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -409,6 +445,15 @@ impl From for PatchMergeJsonError { } } +impl From for ApiCallError { + fn from(error: PatchMergeJsonError) -> Self { + match error { + PatchMergeJsonError::Unexpected(error) => Self::from_api_error("patchMergeJson", error), + PatchMergeJsonError::Transport(error) => Self::from_runtime_error("patchMergeJson", error), + } + } +} + impl std::fmt::Display for PatchMergeJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -447,6 +492,15 @@ impl From for PostTextError { } } +impl From for ApiCallError { + fn from(error: PostTextError) -> Self { + match error { + PostTextError::Unexpected(error) => Self::from_api_error("postText", error), + PostTextError::Transport(error) => Self::from_runtime_error("postText", error), + } + } +} + impl std::fmt::Display for PostTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -485,6 +539,15 @@ impl From for PostXmlError { } } +impl From for ApiCallError { + fn from(error: PostXmlError) -> Self { + match error { + PostXmlError::Unexpected(error) => Self::from_api_error("postXml", error), + PostXmlError::Transport(error) => Self::from_runtime_error("postXml", error), + } + } +} + impl std::fmt::Display for PostXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -524,6 +587,15 @@ impl From for GetXmlResponseError { } } +impl From for ApiCallError { + fn from(error: GetXmlResponseError) -> Self { + match error { + GetXmlResponseError::Unexpected(error) => Self::from_api_error("getXmlResponse", error), + GetXmlResponseError::Transport(error) => Self::from_runtime_error("getXmlResponse", error), + } + } +} + impl std::fmt::Display for GetXmlResponseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/request-body-content-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/request-body-content-types/src/runtime/error.rs.golden index 09294dc4..2432ce0c 100644 --- a/tests/golden/rust/rust-aioduct/request-body-content-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/request-body-content-types/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/apis/widget.rs.golden b/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/apis/widget.rs.golden index c31b14b4..9e998f96 100644 --- a/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Default And Exact Response API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -70,6 +70,16 @@ impl From for GetWidgetError { } } +impl From for ApiCallError { + fn from(error: GetWidgetError) -> Self { + match error { + GetWidgetError::Default(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Unexpected(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Transport(error) => Self::from_runtime_error("getWidget", error), + } + } +} + impl std::fmt::Display for GetWidgetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/runtime/error.rs.golden index c7d3e453..e3b6ab36 100644 --- a/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-default-and-exact/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/response-body-fallback/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/response-body-fallback/src/apis/default.rs.golden index fd6d08d5..ff700a38 100644 --- a/tests/golden/rust/rust-aioduct/response-body-fallback/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-fallback/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Response Fallback Test API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -101,6 +101,16 @@ impl From for GetFallbackNoBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackNoBodyError) -> Self { + match error { + GetFallbackNoBodyError::BadRequest(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Unexpected(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Transport(error) => Self::from_runtime_error("get_fallback_no_body", error), + } + } +} + impl std::fmt::Display for GetFallbackNoBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -142,6 +152,16 @@ impl From for GetFallbackWithBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackWithBodyError) -> Self { + match error { + GetFallbackWithBodyError::NotFound(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Unexpected(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Transport(error) => Self::from_runtime_error("get_fallback_with_body", error), + } + } +} + impl std::fmt::Display for GetFallbackWithBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/response-body-fallback/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/response-body-fallback/src/runtime/error.rs.golden index 1ae8ffb2..10606ed7 100644 --- a/tests/golden/rust/rust-aioduct/response-body-fallback/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-fallback/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/apis/widget.rs.golden b/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/apis/widget.rs.golden index fcb35c8b..63cb0650 100644 --- a/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Multi Status API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -78,6 +78,16 @@ impl From for ListWidgetsError { } } +impl From for ApiCallError { + fn from(error: ListWidgetsError) -> Self { + match error { + ListWidgetsError::NotFound(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Unexpected(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Transport(error) => Self::from_runtime_error("listWidgets", error), + } + } +} + impl std::fmt::Display for ListWidgetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/runtime/error.rs.golden index ead62ceb..d4a035ce 100644 --- a/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-multi-status-responses/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/apis/foo.rs.golden b/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/apis/foo.rs.golden index 7a5cba4a..9822d287 100644 --- a/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/apis/foo.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/apis/foo.rs.golden @@ -4,7 +4,7 @@ // No Response Body API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Foo" tag. pub struct FooApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -78,6 +78,17 @@ impl From for UpdateFooBarError { } } +impl From for ApiCallError { + fn from(error: UpdateFooBarError) -> Self { + match error { + UpdateFooBarError::BadRequest(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::InternalServerError(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Unexpected(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Transport(error) => Self::from_runtime_error("updateFooBar", error), + } + } +} + impl std::fmt::Display for UpdateFooBarError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/runtime/error.rs.golden index c57d3704..3984752c 100644 --- a/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/response-body-no-response-body/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/server-object/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/server-object/src/apis/default.rs.golden index 2f0b15ff..31d84556 100644 --- a/tests/golden/rust/rust-aioduct/server-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/server-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // This is a test API specification that includes various server configurations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -108,6 +108,15 @@ impl From for GetAllUsersError { } } +impl From for ApiCallError { + fn from(error: GetAllUsersError) -> Self { + match error { + GetAllUsersError::Unexpected(error) => Self::from_api_error("getAllUsers", error), + GetAllUsersError::Transport(error) => Self::from_runtime_error("getAllUsers", error), + } + } +} + impl std::fmt::Display for GetAllUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -148,6 +157,16 @@ impl From for GetUserByIdError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdError) -> Self { + match error { + GetUserByIdError::NotFound(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Unexpected(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Transport(error) => Self::from_runtime_error("getUserById", error), + } + } +} + impl std::fmt::Display for GetUserByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/server-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/server-object/src/runtime/error.rs.golden index 6366ecd7..c40c1926 100644 --- a/tests/golden/rust/rust-aioduct/server-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/server-object/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/server-path-prefix/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/server-path-prefix/src/apis/default.rs.golden index 0a2843aa..355ed140 100644 --- a/tests/golden/rust/rust-aioduct/server-path-prefix/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/server-path-prefix/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for server URL path prefix stripping in operation paths. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -115,6 +115,15 @@ impl From for HealthCheckError { } } +impl From for ApiCallError { + fn from(error: HealthCheckError) -> Self { + match error { + HealthCheckError::Unexpected(error) => Self::from_api_error("health_check", error), + HealthCheckError::Transport(error) => Self::from_runtime_error("health_check", error), + } + } +} + impl std::fmt::Display for HealthCheckError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -153,6 +162,15 @@ impl From for ListUsersError { } } +impl From for ApiCallError { + fn from(error: ListUsersError) -> Self { + match error { + ListUsersError::Unexpected(error) => Self::from_api_error("list_users", error), + ListUsersError::Transport(error) => Self::from_runtime_error("list_users", error), + } + } +} + impl std::fmt::Display for ListUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -191,6 +209,15 @@ impl From for GetUserError { } } +impl From for ApiCallError { + fn from(error: GetUserError) -> Self { + match error { + GetUserError::Unexpected(error) => Self::from_api_error("get_user", error), + GetUserError::Transport(error) => Self::from_runtime_error("get_user", error), + } + } +} + impl std::fmt::Display for GetUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/server-path-prefix/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/server-path-prefix/src/runtime/error.rs.golden index 5c4ee1ec..c5d77284 100644 --- a/tests/golden/rust/rust-aioduct/server-path-prefix/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/server-path-prefix/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/apis/default.rs.golden index 97665b12..af16f2f7 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for complex union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestComplexUnionError { } } +impl From for ApiCallError { + fn from(error: TestComplexUnionError) -> Self { + match error { + TestComplexUnionError::Unexpected(error) => Self::from_api_error("test_complex_union", error), + TestComplexUnionError::Transport(error) => Self::from_runtime_error("test_complex_union", error), + } + } +} + impl std::fmt::Display for TestComplexUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/runtime/error.rs.golden index 8c9f6914..e9d91613 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-complex-union/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden index ece2fb84..1f319004 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden @@ -14,7 +14,7 @@ // "EventKindUnspecified" instead of generic "Kind". use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -81,6 +81,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden index e00a9ac2..edba25ba 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden @@ -114,3 +114,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden index aaf12b81..e2eeecd9 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // See: https://www.openapis.org/understanding-openapi/specification/models/ use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -74,6 +74,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden index 2c3b7d23..27246ea2 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden @@ -107,3 +107,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden index fa664d89..1ecbb2ea 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // 100-char render width. Ensures PEP 695 parenthesization is correct. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -74,6 +74,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden index 43207987..21bdd8ed 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden @@ -107,3 +107,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden index 29b03d85..9aba2610 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden @@ -10,7 +10,7 @@ // is a unit variant (no fields) and others carry data from referenced schemas. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -77,6 +77,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden index da1befe2..7fa31493 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden @@ -110,3 +110,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden index f1047260..ebe39201 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden @@ -8,7 +8,7 @@ // the same Kind.ts file causing conflicts. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -105,6 +105,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -144,6 +153,15 @@ impl From for CreateVolumeError { } } +impl From for ApiCallError { + fn from(error: CreateVolumeError) -> Self { + match error { + CreateVolumeError::Unexpected(error) => Self::from_api_error("create_volume", error), + CreateVolumeError::Transport(error) => Self::from_runtime_error("create_volume", error), + } + } +} + impl std::fmt::Display for CreateVolumeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden index 885fb933..e960dbfe 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden @@ -108,3 +108,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden index 874cccb5..1d182578 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // This is similar to the ContainerImage pattern in the infiron API. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -73,6 +73,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden index 25cbb488..224e1a36 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden @@ -106,3 +106,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/apis/default.rs.golden index 90ec7185..d980737f 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestIntersectionError { } } +impl From for ApiCallError { + fn from(error: TestIntersectionError) -> Self { + match error { + TestIntersectionError::Unexpected(error) => Self::from_api_error("test_intersection", error), + TestIntersectionError::Transport(error) => Self::from_runtime_error("test_intersection", error), + } + } +} + impl std::fmt::Display for TestIntersectionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/runtime/error.rs.golden index 8473435c..e548ae5c 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-intersection-allof/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden index 01d79f49..df454180 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types with nullable reference properties use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -102,6 +102,15 @@ impl From for CreateTestError { } } +impl From for ApiCallError { + fn from(error: CreateTestError) -> Self { + match error { + CreateTestError::Unexpected(error) => Self::from_api_error("create_test", error), + CreateTestError::Transport(error) => Self::from_runtime_error("create_test", error), + } + } +} + impl std::fmt::Display for CreateTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -141,6 +150,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("get_test", error), + GetTestError::Transport(error) => Self::from_runtime_error("get_test", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden index 31d6471b..463cb166 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/apis/default.rs.golden index 7d0d88e7..227b6d53 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that reference other union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestNestedUnionError { } } +impl From for ApiCallError { + fn from(error: TestNestedUnionError) -> Self { + match error { + TestNestedUnionError::Unexpected(error) => Self::from_api_error("test_nested_union", error), + TestNestedUnionError::Transport(error) => Self::from_runtime_error("test_nested_union", error), + } + } +} + impl std::fmt::Display for TestNestedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/runtime/error.rs.golden index 9cc57dbb..23e49df7 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-nested-union/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/apis/default.rs.golden index f780b102..1a686950 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for simple type aliases (not unions or intersections) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestSimpleAliasError { } } +impl From for ApiCallError { + fn from(error: TestSimpleAliasError) -> Self { + match error { + TestSimpleAliasError::Unexpected(error) => Self::from_api_error("test_simple_alias", error), + TestSimpleAliasError::Transport(error) => Self::from_runtime_error("test_simple_alias", error), + } + } +} + impl std::fmt::Display for TestSimpleAliasError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/runtime/error.rs.golden index 3f27e56a..f2cc74d6 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-simple-type-alias/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/apis/default.rs.golden index 5d3ad8e3..809891c4 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with both interfaces and primitives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestMixedUnionError { } } +impl From for ApiCallError { + fn from(error: TestMixedUnionError) -> Self { + match error { + TestMixedUnionError::Unexpected(error) => Self::from_api_error("test_mixed_union", error), + TestMixedUnionError::Transport(error) => Self::from_runtime_error("test_mixed_union", error), + } + } +} + impl std::fmt::Display for TestMixedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/runtime/error.rs.golden index b8654d10..8c1b2f31 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-mixed/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/apis/default.rs.golden index 0f7407bc..a5c7c38a 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that include the any type use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestUnionWithAnyError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithAnyError) -> Self { + match error { + TestUnionWithAnyError::Unexpected(error) => Self::from_api_error("test_union_with_any", error), + TestUnionWithAnyError::Transport(error) => Self::from_runtime_error("test_union_with_any", error), + } + } +} + impl std::fmt::Display for TestUnionWithAnyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/runtime/error.rs.golden index e5552e0e..0cc9fe1a 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-any/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/apis/default.rs.golden index 508fb3ed..eef790c1 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types (oneOf) with inline object schemas that should generate named interfaces use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestUnionWithInlineObjectsError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithInlineObjectsError) -> Self { + match error { + TestUnionWithInlineObjectsError::Unexpected(error) => Self::from_api_error("test_union_with_inline_objects", error), + TestUnionWithInlineObjectsError::Transport(error) => Self::from_runtime_error("test_union_with_inline_objects", error), + } + } +} + impl std::fmt::Display for TestUnionWithInlineObjectsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden index 79d55728..69e8f6fa 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/apis/default.rs.golden index 17ad20dc..e8e36924 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for oneOf/anyOf union types with interface members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestUnionError { } } +impl From for ApiCallError { + fn from(error: TestUnionError) -> Self { + match error { + TestUnionError::Unexpected(error) => Self::from_api_error("test_union", error), + TestUnionError::Transport(error) => Self::from_runtime_error("test_union", error), + } + } +} + impl std::fmt::Display for TestUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/runtime/error.rs.golden index c3124fa1..029e4427 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-interfaces/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/apis/default.rs.golden index 62a85120..57fbd6be 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with primitive members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for TestUnionPrimitivesError { } } +impl From for ApiCallError { + fn from(error: TestUnionPrimitivesError) -> Self { + match error { + TestUnionPrimitivesError::Unexpected(error) => Self::from_api_error("test_union_primitives", error), + TestUnionPrimitivesError::Transport(error) => Self::from_runtime_error("test_union_primitives", error), + } + } +} + impl std::fmt::Display for TestUnionPrimitivesError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/runtime/error.rs.golden index 8d652e88..5de3faea 100644 --- a/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/type-aliases-union-with-primitives/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/typed-error-responses/src/apis/resources.rs.golden b/tests/golden/rust/rust-aioduct/typed-error-responses/src/apis/resources.rs.golden index 5a1f0f84..cae1ee23 100644 --- a/tests/golden/rust/rust-aioduct/typed-error-responses/src/apis/resources.rs.golden +++ b/tests/golden/rust/rust-aioduct/typed-error-responses/src/apis/resources.rs.golden @@ -4,7 +4,7 @@ // Typed Error Responses API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Resources" tag. pub struct ResourcesApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -170,6 +170,16 @@ impl From for CheckNoSuccessBodyError { } } +impl From for ApiCallError { + fn from(error: CheckNoSuccessBodyError) -> Self { + match error { + CheckNoSuccessBodyError::ServiceUnavailable(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Unexpected(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Transport(error) => Self::from_runtime_error("checkNoSuccessBody", error), + } + } +} + impl CheckNoSuccessBodyError { fn response_headers(&self) -> Option<&aioduct::HeaderMap> { match self { @@ -246,6 +256,23 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::BadRequest(error) => Self::from_api_error("createResource", error), + CreateResourceError::Conflict(error) => Self::from_api_error("createResource", error), + CreateResourceError::Validation(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status423(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status424(error) => Self::from_api_error("createResource", error), + CreateResourceError::TooManyRequests(error) => Self::from_api_error("createResource", error), + CreateResourceError::ServerError(error) => Self::from_api_error("createResource", error), + CreateResourceError::Default(error) => Self::from_api_error("createResource", error), + CreateResourceError::Unexpected(error) => Self::from_api_error("createResource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("createResource", error), + } + } +} + impl CreateResourceError { fn response_headers(&self) -> Option<&aioduct::HeaderMap> { match self { @@ -336,6 +363,16 @@ impl From for CreateResourceWithWildcardSuccessError { } } +impl From for ApiCallError { + fn from(error: CreateResourceWithWildcardSuccessError) -> Self { + match error { + CreateResourceWithWildcardSuccessError::BadRequest(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Unexpected(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Transport(error) => Self::from_runtime_error("createResourceWithWildcardSuccess", error), + } + } +} + impl std::fmt::Display for CreateResourceWithWildcardSuccessError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/typed-error-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/typed-error-responses/src/runtime/error.rs.golden index 22410a51..0ec53e7f 100644 --- a/tests/golden/rust/rust-aioduct/typed-error-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/typed-error-responses/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/utoipa-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-mixed/src/apis/default.rs.golden index e50bcfff..979741a7 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture covering all schema kinds with utoipa enabled use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -169,6 +169,15 @@ impl From for CreateFeedError { } } +impl From for ApiCallError { + fn from(error: CreateFeedError) -> Self { + match error { + CreateFeedError::Unexpected(error) => Self::from_api_error("create_feed", error), + CreateFeedError::Transport(error) => Self::from_runtime_error("create_feed", error), + } + } +} + impl std::fmt::Display for CreateFeedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -208,6 +217,15 @@ impl From for CreatePaymentError { } } +impl From for ApiCallError { + fn from(error: CreatePaymentError) -> Self { + match error { + CreatePaymentError::Unexpected(error) => Self::from_api_error("create_payment", error), + CreatePaymentError::Transport(error) => Self::from_runtime_error("create_payment", error), + } + } +} + impl std::fmt::Display for CreatePaymentError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -247,6 +265,15 @@ impl From for ListPetsError { } } +impl From for ApiCallError { + fn from(error: ListPetsError) -> Self { + match error { + ListPetsError::Unexpected(error) => Self::from_api_error("list_pets", error), + ListPetsError::Transport(error) => Self::from_runtime_error("list_pets", error), + } + } +} + impl std::fmt::Display for ListPetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -286,6 +313,15 @@ impl From for CreatePetError { } } +impl From for ApiCallError { + fn from(error: CreatePetError) -> Self { + match error { + CreatePetError::Unexpected(error) => Self::from_api_error("create_pet", error), + CreatePetError::Transport(error) => Self::from_runtime_error("create_pet", error), + } + } +} + impl std::fmt::Display for CreatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/utoipa-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-mixed/src/runtime/error.rs.golden index ed8bf9d9..61b4d322 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-mixed/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/apis/default.rs.golden index 31e929d9..3235a913 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for untagged union manual utoipa impl generation use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/runtime/error.rs.golden index aba9be21..3d43a9c0 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-untagged-union/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/apis/default.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/apis/default.rs.golden index 780c6e0e..6b4ce179 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test that utoipa::ToSchema is additive alongside user extra_derives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a, R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> { @@ -72,6 +72,15 @@ impl From for CreateItemError { } } +impl From for ApiCallError { + fn from(error: CreateItemError) -> Self { + match error { + CreateItemError::Unexpected(error) => Self::from_api_error("create_item", error), + CreateItemError::Transport(error) => Self::from_runtime_error("create_item", error), + } + } +} + impl std::fmt::Display for CreateItemError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/runtime/error.rs.golden b/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/runtime/error.rs.golden index 72caeb49..cc375efb 100644 --- a/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-aioduct/utoipa-with-extra-derives/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: aioduct::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&aioduct::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/additional-properties/src/apis/additional_properties.rs.golden b/tests/golden/rust/rust-reqwest/additional-properties/src/apis/additional_properties.rs.golden index 508eb395..4faa8826 100644 --- a/tests/golden/rust/rust-reqwest/additional-properties/src/apis/additional_properties.rs.golden +++ b/tests/golden/rust/rust-reqwest/additional-properties/src/apis/additional_properties.rs.golden @@ -5,7 +5,7 @@ // API demonstrating OpenAPI additionalProperties with multiple levels of structs (RootLevel -> MiddleLevel -> LeafValue), each with HashMap fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "additional-properties" tag. pub struct AdditionalPropertiesApi<'a> { @@ -145,6 +145,16 @@ impl From for PostLeafError { } } +impl From for ApiCallError { + fn from(error: PostLeafError) -> Self { + match error { + PostLeafError::BadRequest(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Unexpected(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Transport(error) => Self::from_runtime_error("post_leaf", error), + } + } +} + impl std::fmt::Display for PostLeafError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -186,6 +196,16 @@ impl From for PostMiddleError { } } +impl From for ApiCallError { + fn from(error: PostMiddleError) -> Self { + match error { + PostMiddleError::BadRequest(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Unexpected(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Transport(error) => Self::from_runtime_error("post_middle", error), + } + } +} + impl std::fmt::Display for PostMiddleError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -227,6 +247,16 @@ impl From for PostRootError { } } +impl From for ApiCallError { + fn from(error: PostRootError) -> Self { + match error { + PostRootError::BadRequest(error) => Self::from_api_error("post_root", error), + PostRootError::Unexpected(error) => Self::from_api_error("post_root", error), + PostRootError::Transport(error) => Self::from_runtime_error("post_root", error), + } + } +} + impl std::fmt::Display for PostRootError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/additional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/additional-properties/src/runtime/error.rs.golden index 5b32d610..fd33cedc 100644 --- a/tests/golden/rust/rust-reqwest/additional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/additional-properties/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/apis/transfer.rs.golden b/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/apis/transfer.rs.golden index 8455dd1a..e6d283ba 100644 --- a/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart upload and octet-stream download. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -115,6 +115,16 @@ impl From for DownloadAssetError { } } +impl From for ApiCallError { + fn from(error: DownloadAssetError) -> Self { + match error { + DownloadAssetError::NotFound(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Unexpected(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Transport(error) => Self::from_runtime_error("download_asset", error), + } + } +} + impl std::fmt::Display for DownloadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -156,6 +166,16 @@ impl From for UploadAssetError { } } +impl From for ApiCallError { + fn from(error: UploadAssetError) -> Self { + match error { + UploadAssetError::BadRequest(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Unexpected(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Transport(error) => Self::from_runtime_error("upload_asset", error), + } + } +} + impl std::fmt::Display for UploadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/runtime/error.rs.golden index 5bc38198..00dda373 100644 --- a/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/binary-transfer-media-types/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/apis/default.rs.golden index f8f60300..6701291d 100644 --- a/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Comprehensive test for all OpenAPI v3.1.2 schema types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/runtime/error.rs.golden index 275c46d7..6c087459 100644 --- a/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/comprehensive-schemas/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/apis/test_resource.rs.golden b/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/apis/test_resource.rs.golden index c0f667f3..2b029592 100644 --- a/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/apis/test_resource.rs.golden +++ b/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/apis/test_resource.rs.golden @@ -5,7 +5,7 @@ // Test fixture for DELETE operations with JSON response schemas and type alias request bodies use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-resource" tag. pub struct TestResourceApi<'a> { @@ -110,6 +110,15 @@ impl From for CreateTestResourceError { } } +impl From for ApiCallError { + fn from(error: CreateTestResourceError) -> Self { + match error { + CreateTestResourceError::Unexpected(error) => Self::from_api_error("create_test_resource", error), + CreateTestResourceError::Transport(error) => Self::from_runtime_error("create_test_resource", error), + } + } +} + impl std::fmt::Display for CreateTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -151,6 +160,17 @@ impl From for DeleteTestResourceError { } } +impl From for ApiCallError { + fn from(error: DeleteTestResourceError) -> Self { + match error { + DeleteTestResourceError::ClientError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::ServerError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Unexpected(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Transport(error) => Self::from_runtime_error("delete_test_resource", error), + } + } +} + impl std::fmt::Display for DeleteTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/runtime/error.rs.golden index 892352b2..64bd2e28 100644 --- a/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/delete-with-response-schema/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/items.rs.golden b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/items.rs.golden index cfbc04ab..eead2313 100644 --- a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a> { @@ -88,6 +88,16 @@ impl From for CreateItemWithBodyConflictError { } } +impl From for ApiCallError { + fn from(error: CreateItemWithBodyConflictError) -> Self { + match error { + CreateItemWithBodyConflictError::BadRequest(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Unexpected(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Transport(error) => Self::from_runtime_error("create_item_with_body_conflict", error), + } + } +} + impl std::fmt::Display for CreateItemWithBodyConflictError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/users.rs.golden b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/users.rs.golden index 49951635..3146153b 100644 --- a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/users.rs.golden +++ b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/apis/users.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "users" tag. pub struct UsersApi<'a> { @@ -95,6 +95,17 @@ impl From for GetUserByIdDuplicateError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdDuplicateError) -> Self { + match error { + GetUserByIdDuplicateError::BadRequest(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::NotFound(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Unexpected(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Transport(error) => Self::from_runtime_error("get_user_by_id_duplicate", error), + } + } +} + impl std::fmt::Display for GetUserByIdDuplicateError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/runtime/error.rs.golden index ef830212..3fcf4ce7 100644 --- a/tests/golden/rust/rust-reqwest/duplicate-param-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/duplicate-param-names/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/enum-repr/src/apis/enum_repr.rs.golden b/tests/golden/rust/rust-reqwest/enum-repr/src/apis/enum_repr.rs.golden index aa7bb06c..50a30f52 100644 --- a/tests/golden/rust/rust-reqwest/enum-repr/src/apis/enum_repr.rs.golden +++ b/tests/golden/rust/rust-reqwest/enum-repr/src/apis/enum_repr.rs.golden @@ -5,7 +5,7 @@ // This API demonstrates all 4 kinds of enum representation types: Externally Tagged, Internally Tagged, Adjacently Tagged, and Untagged use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "enum-repr" tag. pub struct EnumReprApi<'a> { @@ -213,6 +213,16 @@ impl From for HandleAdjacentlyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleAdjacentlyTaggedError) -> Self { + match error { + HandleAdjacentlyTaggedError::BadRequest(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Unexpected(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Transport(error) => Self::from_runtime_error("handle_adjacently_tagged", error), + } + } +} + impl std::fmt::Display for HandleAdjacentlyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -254,6 +264,16 @@ impl From for HandleExternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleExternallyTaggedError) -> Self { + match error { + HandleExternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_externally_tagged", error), + } + } +} + impl std::fmt::Display for HandleExternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -295,6 +315,16 @@ impl From for HandleInternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleInternallyTaggedError) -> Self { + match error { + HandleInternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_internally_tagged", error), + } + } +} + impl std::fmt::Display for HandleInternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -336,6 +366,16 @@ impl From for HandleMixedError { } } +impl From for ApiCallError { + fn from(error: HandleMixedError) -> Self { + match error { + HandleMixedError::BadRequest(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Unexpected(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Transport(error) => Self::from_runtime_error("handle_mixed", error), + } + } +} + impl std::fmt::Display for HandleMixedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -377,6 +417,16 @@ impl From for HandleUntaggedError { } } +impl From for ApiCallError { + fn from(error: HandleUntaggedError) -> Self { + match error { + HandleUntaggedError::BadRequest(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Unexpected(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Transport(error) => Self::from_runtime_error("handle_untagged", error), + } + } +} + impl std::fmt::Display for HandleUntaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/enum-repr/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/enum-repr/src/runtime/error.rs.golden index 49438bcb..810cf9e1 100644 --- a/tests/golden/rust/rust-reqwest/enum-repr/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/enum-repr/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/media-type-selection/src/apis/media.rs.golden b/tests/golden/rust/rust-reqwest/media-type-selection/src/apis/media.rs.golden index 7e791994..09053aa1 100644 --- a/tests/golden/rust/rust-reqwest/media-type-selection/src/apis/media.rs.golden +++ b/tests/golden/rust/rust-reqwest/media-type-selection/src/apis/media.rs.golden @@ -5,7 +5,7 @@ // Covers normalized media-type selection for requests and responses. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "media" tag. pub struct MediaApi<'a> { @@ -208,6 +208,15 @@ impl From for SendJsonPreferredError { } } +impl From for ApiCallError { + fn from(error: SendJsonPreferredError) -> Self { + match error { + SendJsonPreferredError::Unexpected(error) => Self::from_api_error("send_json_preferred", error), + SendJsonPreferredError::Transport(error) => Self::from_runtime_error("send_json_preferred", error), + } + } +} + impl std::fmt::Display for SendJsonPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -246,6 +255,15 @@ impl From for SendParameterizedMultipartError { } } +impl From for ApiCallError { + fn from(error: SendParameterizedMultipartError) -> Self { + match error { + SendParameterizedMultipartError::Unexpected(error) => Self::from_api_error("send_parameterized_multipart", error), + SendParameterizedMultipartError::Transport(error) => Self::from_runtime_error("send_parameterized_multipart", error), + } + } +} + impl std::fmt::Display for SendParameterizedMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -284,6 +302,15 @@ impl From for SendVendorJsonError { } } +impl From for ApiCallError { + fn from(error: SendVendorJsonError) -> Self { + match error { + SendVendorJsonError::Unexpected(error) => Self::from_api_error("send_vendor_json", error), + SendVendorJsonError::Transport(error) => Self::from_runtime_error("send_vendor_json", error), + } + } +} + impl std::fmt::Display for SendVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -323,6 +350,15 @@ impl From for GetOctetPreferredError { } } +impl From for ApiCallError { + fn from(error: GetOctetPreferredError) -> Self { + match error { + GetOctetPreferredError::Unexpected(error) => Self::from_api_error("get_octet_preferred", error), + GetOctetPreferredError::Transport(error) => Self::from_runtime_error("get_octet_preferred", error), + } + } +} + impl std::fmt::Display for GetOctetPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -362,6 +398,15 @@ impl From for GetTextPreferredError { } } +impl From for ApiCallError { + fn from(error: GetTextPreferredError) -> Self { + match error { + GetTextPreferredError::Unexpected(error) => Self::from_api_error("get_text_preferred", error), + GetTextPreferredError::Transport(error) => Self::from_runtime_error("get_text_preferred", error), + } + } +} + impl std::fmt::Display for GetTextPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -401,6 +446,15 @@ impl From for GetVendorJsonError { } } +impl From for ApiCallError { + fn from(error: GetVendorJsonError) -> Self { + match error { + GetVendorJsonError::Unexpected(error) => Self::from_api_error("get_vendor_json", error), + GetVendorJsonError::Transport(error) => Self::from_runtime_error("get_vendor_json", error), + } + } +} + impl std::fmt::Display for GetVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/media-type-selection/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/media-type-selection/src/runtime/error.rs.golden index 65540a9d..df2d6eef 100644 --- a/tests/golden/rust/rust-reqwest/media-type-selection/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/media-type-selection/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/minimal/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/minimal/src/apis/default.rs.golden index 28fbf462..ccc1430c 100644 --- a/tests/golden/rust/rust-reqwest/minimal/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/minimal/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Minimal API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -64,6 +64,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("getTest", error), + GetTestError::Transport(error) => Self::from_runtime_error("getTest", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/minimal/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/minimal/src/runtime/error.rs.golden index a4f3d885..390f7113 100644 --- a/tests/golden/rust/rust-reqwest/minimal/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/minimal/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/apis/default.rs.golden index db2e4c0e..db52f6f8 100644 --- a/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // and primitive type alias suppression. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -111,6 +111,15 @@ impl From for AcceptNodeError { } } +impl From for ApiCallError { + fn from(error: AcceptNodeError) -> Self { + match error { + AcceptNodeError::Unexpected(error) => Self::from_api_error("acceptNode", error), + AcceptNodeError::Transport(error) => Self::from_runtime_error("acceptNode", error), + } + } +} + impl std::fmt::Display for AcceptNodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -150,6 +159,15 @@ impl From for GetNodeNameError { } } +impl From for ApiCallError { + fn from(error: GetNodeNameError) -> Self { + match error { + GetNodeNameError::Unexpected(error) => Self::from_api_error("getNodeName", error), + GetNodeNameError::Transport(error) => Self::from_runtime_error("getNodeName", error), + } + } +} + impl std::fmt::Display for GetNodeNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden index e2c53c06..b3779e86 100644 --- a/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/apis/multipart.rs.golden b/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/apis/multipart.rs.golden index 932915ec..6f043ab0 100644 --- a/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers optional multipart bodies, optional parts, and text-only multipart fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a> { @@ -115,6 +115,15 @@ impl From for SendOptionalPartsError { } } +impl From for ApiCallError { + fn from(error: SendOptionalPartsError) -> Self { + match error { + SendOptionalPartsError::Unexpected(error) => Self::from_api_error("send_optional_parts", error), + SendOptionalPartsError::Transport(error) => Self::from_runtime_error("send_optional_parts", error), + } + } +} + impl std::fmt::Display for SendOptionalPartsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -153,6 +162,15 @@ impl From for SendTextFieldsError { } } +impl From for ApiCallError { + fn from(error: SendTextFieldsError) -> Self { + match error { + SendTextFieldsError::Unexpected(error) => Self::from_api_error("send_text_fields", error), + SendTextFieldsError::Transport(error) => Self::from_runtime_error("send_text_fields", error), + } + } +} + impl std::fmt::Display for SendTextFieldsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/runtime/error.rs.golden index 0a9757dd..ddcac1bb 100644 --- a/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-edge-cases/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/apis/transfer.rs.golden b/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/apis/transfer.rs.golden index 11c6e393..f9037d00 100644 --- a/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers explicit multipart part content types. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -72,6 +72,15 @@ impl From for UploadEncodedAssetError { } } +impl From for ApiCallError { + fn from(error: UploadEncodedAssetError) -> Self { + match error { + UploadEncodedAssetError::Unexpected(error) => Self::from_api_error("upload_encoded_asset", error), + UploadEncodedAssetError::Transport(error) => Self::from_runtime_error("upload_encoded_asset", error), + } + } +} + impl std::fmt::Display for UploadEncodedAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/runtime/error.rs.golden index 4be568bc..6a5b31ad 100644 --- a/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-explicit-encoding/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/apis/multipart.rs.golden b/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/apis/multipart.rs.golden index 4193a5eb..ced429fa 100644 --- a/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers multipart object parts whose wire names differ from ergonomic names. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a> { @@ -70,6 +70,15 @@ impl From for SendNestedObjectPartError { } } +impl From for ApiCallError { + fn from(error: SendNestedObjectPartError) -> Self { + match error { + SendNestedObjectPartError::Unexpected(error) => Self::from_api_error("send_nested_object_part", error), + SendNestedObjectPartError::Transport(error) => Self::from_runtime_error("send_nested_object_part", error), + } + } +} + impl std::fmt::Display for SendNestedObjectPartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/runtime/error.rs.golden index c3f32ef4..9132a7e8 100644 --- a/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-nested-object-parts/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/apis/transfer.rs.golden b/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/apis/transfer.rs.golden index fad7945d..20e169e6 100644 --- a/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart request bodies that are not object-shaped. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -51,6 +51,15 @@ impl From for UploadRawMultipartError { } } +impl From for ApiCallError { + fn from(error: UploadRawMultipartError) -> Self { + match error { + UploadRawMultipartError::Unexpected(error) => Self::from_api_error("upload_raw_multipart", error), + UploadRawMultipartError::Transport(error) => Self::from_runtime_error("upload_raw_multipart", error), + } + } +} + impl std::fmt::Display for UploadRawMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/runtime/error.rs.golden index dee1197f..f47b4e3a 100644 --- a/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multipart-unsupported-schema/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/apis/test_api.rs.golden b/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/apis/test_api.rs.golden index be63675c..07ae7384 100644 --- a/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/apis/test_api.rs.golden +++ b/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/apis/test_api.rs.golden @@ -5,7 +5,7 @@ // Test API with multiple operations having similar request body schema names use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-api" tag. pub struct TestApiApi<'a> { @@ -102,6 +102,15 @@ impl From for CreateTypeAError { } } +impl From for ApiCallError { + fn from(error: CreateTypeAError) -> Self { + match error { + CreateTypeAError::Unexpected(error) => Self::from_api_error("create_type_a", error), + CreateTypeAError::Transport(error) => Self::from_runtime_error("create_type_a", error), + } + } +} + impl std::fmt::Display for CreateTypeAError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -141,6 +150,15 @@ impl From for CreateTypeBError { } } +impl From for ApiCallError { + fn from(error: CreateTypeBError) -> Self { + match error { + CreateTypeBError::Unexpected(error) => Self::from_api_error("create_type_b", error), + CreateTypeBError::Transport(error) => Self::from_runtime_error("create_type_b", error), + } + } +} + impl std::fmt::Display for CreateTypeBError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/runtime/error.rs.golden index fa83ed86..dd673104 100644 --- a/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/multiple-similar-request-schemas/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/naming-conventions/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/naming-conventions/src/apis/default.rs.golden index e19f9663..d91a94ba 100644 --- a/tests/golden/rust/rust-reqwest/naming-conventions/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/naming-conventions/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture to verify language property naming conventions use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -146,6 +146,15 @@ impl From for GetWithQueryParamsError { } } +impl From for ApiCallError { + fn from(error: GetWithQueryParamsError) -> Self { + match error { + GetWithQueryParamsError::Unexpected(error) => Self::from_api_error("get_with_query_params", error), + GetWithQueryParamsError::Transport(error) => Self::from_runtime_error("get_with_query_params", error), + } + } +} + impl std::fmt::Display for GetWithQueryParamsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -185,6 +194,15 @@ impl From for TestNamingConventionsError { } } +impl From for ApiCallError { + fn from(error: TestNamingConventionsError) -> Self { + match error { + TestNamingConventionsError::Unexpected(error) => Self::from_api_error("test_naming_conventions", error), + TestNamingConventionsError::Transport(error) => Self::from_runtime_error("test_naming_conventions", error), + } + } +} + impl std::fmt::Display for TestNamingConventionsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/naming-conventions/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/naming-conventions/src/runtime/error.rs.golden index 9fc0f9cf..8b302361 100644 --- a/tests/golden/rust/rust-reqwest/naming-conventions/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/naming-conventions/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/optional-request-bodies/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/optional-request-bodies/src/apis/default.rs.golden index 8b6977f7..2929b617 100644 --- a/tests/golden/rust/rust-reqwest/optional-request-bodies/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/optional-request-bodies/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers optional non-multipart request bodies. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -153,6 +153,15 @@ impl From for SendOptionalBinaryError { } } +impl From for ApiCallError { + fn from(error: SendOptionalBinaryError) -> Self { + match error { + SendOptionalBinaryError::Unexpected(error) => Self::from_api_error("send_optional_binary", error), + SendOptionalBinaryError::Transport(error) => Self::from_runtime_error("send_optional_binary", error), + } + } +} + impl std::fmt::Display for SendOptionalBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -191,6 +200,15 @@ impl From for SendOptionalJsonError { } } +impl From for ApiCallError { + fn from(error: SendOptionalJsonError) -> Self { + match error { + SendOptionalJsonError::Unexpected(error) => Self::from_api_error("send_optional_json", error), + SendOptionalJsonError::Transport(error) => Self::from_runtime_error("send_optional_json", error), + } + } +} + impl std::fmt::Display for SendOptionalJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -229,6 +247,15 @@ impl From for SendOptionalTextError { } } +impl From for ApiCallError { + fn from(error: SendOptionalTextError) -> Self { + match error { + SendOptionalTextError::Unexpected(error) => Self::from_api_error("send_optional_text", error), + SendOptionalTextError::Transport(error) => Self::from_runtime_error("send_optional_text", error), + } + } +} + impl std::fmt::Display for SendOptionalTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -267,6 +294,15 @@ impl From for SendRequiredJsonError { } } +impl From for ApiCallError { + fn from(error: SendRequiredJsonError) -> Self { + match error { + SendRequiredJsonError::Unexpected(error) => Self::from_api_error("send_required_json", error), + SendRequiredJsonError::Transport(error) => Self::from_runtime_error("send_required_json", error), + } + } +} + impl std::fmt::Display for SendRequiredJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/optional-request-bodies/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/optional-request-bodies/src/runtime/error.rs.golden index fb841411..17a4f0c8 100644 --- a/tests/golden/rust/rust-reqwest/optional-request-bodies/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/optional-request-bodies/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/petstore/src/apis/pet.rs.golden b/tests/golden/rust/rust-reqwest/petstore/src/apis/pet.rs.golden index 3a34c5b4..af90d933 100644 --- a/tests/golden/rust/rust-reqwest/petstore/src/apis/pet.rs.golden +++ b/tests/golden/rust/rust-reqwest/petstore/src/apis/pet.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "pet" tag. pub struct PetApi<'a> { @@ -357,6 +357,18 @@ impl From for UpdatePetError { } } +impl From for ApiCallError { + fn from(error: UpdatePetError) -> Self { + match error { + UpdatePetError::BadRequest(error) => Self::from_api_error("update_pet", error), + UpdatePetError::NotFound(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Validation(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Unexpected(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Transport(error) => Self::from_runtime_error("update_pet", error), + } + } +} + impl std::fmt::Display for UpdatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -401,6 +413,17 @@ impl From for AddPetError { } } +impl From for ApiCallError { + fn from(error: AddPetError) -> Self { + match error { + AddPetError::BadRequest(error) => Self::from_api_error("add_pet", error), + AddPetError::Validation(error) => Self::from_api_error("add_pet", error), + AddPetError::Unexpected(error) => Self::from_api_error("add_pet", error), + AddPetError::Transport(error) => Self::from_runtime_error("add_pet", error), + } + } +} + impl std::fmt::Display for AddPetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -443,6 +466,16 @@ impl From for FindPetsByStatusError { } } +impl From for ApiCallError { + fn from(error: FindPetsByStatusError) -> Self { + match error { + FindPetsByStatusError::BadRequest(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Unexpected(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Transport(error) => Self::from_runtime_error("find_pets_by_status", error), + } + } +} + impl std::fmt::Display for FindPetsByStatusError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -484,6 +517,16 @@ impl From for FindPetsByTagsError { } } +impl From for ApiCallError { + fn from(error: FindPetsByTagsError) -> Self { + match error { + FindPetsByTagsError::BadRequest(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Unexpected(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Transport(error) => Self::from_runtime_error("find_pets_by_tags", error), + } + } +} + impl std::fmt::Display for FindPetsByTagsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -526,6 +569,17 @@ impl From for GetPetByIdError { } } +impl From for ApiCallError { + fn from(error: GetPetByIdError) -> Self { + match error { + GetPetByIdError::BadRequest(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::NotFound(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Unexpected(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Transport(error) => Self::from_runtime_error("get_pet_by_id", error), + } + } +} + impl std::fmt::Display for GetPetByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -568,6 +622,16 @@ impl From for UpdatePetWithFormError { } } +impl From for ApiCallError { + fn from(error: UpdatePetWithFormError) -> Self { + match error { + UpdatePetWithFormError::BadRequest(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Unexpected(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Transport(error) => Self::from_runtime_error("update_pet_with_form", error), + } + } +} + impl std::fmt::Display for UpdatePetWithFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -608,6 +672,16 @@ impl From for DeletePetError { } } +impl From for ApiCallError { + fn from(error: DeletePetError) -> Self { + match error { + DeletePetError::BadRequest(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Unexpected(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Transport(error) => Self::from_runtime_error("delete_pet", error), + } + } +} + impl std::fmt::Display for DeletePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -648,6 +722,15 @@ impl From for UploadFileError { } } +impl From for ApiCallError { + fn from(error: UploadFileError) -> Self { + match error { + UploadFileError::Unexpected(error) => Self::from_api_error("upload_file", error), + UploadFileError::Transport(error) => Self::from_runtime_error("upload_file", error), + } + } +} + impl std::fmt::Display for UploadFileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/petstore/src/apis/store.rs.golden b/tests/golden/rust/rust-reqwest/petstore/src/apis/store.rs.golden index 5e1968ad..50de5df1 100644 --- a/tests/golden/rust/rust-reqwest/petstore/src/apis/store.rs.golden +++ b/tests/golden/rust/rust-reqwest/petstore/src/apis/store.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "store" tag. pub struct StoreApi<'a> { @@ -176,6 +176,15 @@ impl From for GetInventoryError { } } +impl From for ApiCallError { + fn from(error: GetInventoryError) -> Self { + match error { + GetInventoryError::Unexpected(error) => Self::from_api_error("get_inventory", error), + GetInventoryError::Transport(error) => Self::from_runtime_error("get_inventory", error), + } + } +} + impl std::fmt::Display for GetInventoryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -216,6 +225,16 @@ impl From for PlaceOrderError { } } +impl From for ApiCallError { + fn from(error: PlaceOrderError) -> Self { + match error { + PlaceOrderError::BadRequest(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Unexpected(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Transport(error) => Self::from_runtime_error("place_order", error), + } + } +} + impl std::fmt::Display for PlaceOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -258,6 +277,17 @@ impl From for GetOrderByIdError { } } +impl From for ApiCallError { + fn from(error: GetOrderByIdError) -> Self { + match error { + GetOrderByIdError::BadRequest(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::NotFound(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Unexpected(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Transport(error) => Self::from_runtime_error("get_order_by_id", error), + } + } +} + impl std::fmt::Display for GetOrderByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -300,6 +330,17 @@ impl From for DeleteOrderError { } } +impl From for ApiCallError { + fn from(error: DeleteOrderError) -> Self { + match error { + DeleteOrderError::BadRequest(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::NotFound(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Unexpected(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Transport(error) => Self::from_runtime_error("delete_order", error), + } + } +} + impl std::fmt::Display for DeleteOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/petstore/src/apis/user.rs.golden b/tests/golden/rust/rust-reqwest/petstore/src/apis/user.rs.golden index 5b26e1c5..5c2b3a53 100644 --- a/tests/golden/rust/rust-reqwest/petstore/src/apis/user.rs.golden +++ b/tests/golden/rust/rust-reqwest/petstore/src/apis/user.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "user" tag. pub struct UserApi<'a> { @@ -275,6 +275,15 @@ impl From for CreateUserError { } } +impl From for ApiCallError { + fn from(error: CreateUserError) -> Self { + match error { + CreateUserError::Unexpected(error) => Self::from_api_error("create_user", error), + CreateUserError::Transport(error) => Self::from_runtime_error("create_user", error), + } + } +} + impl std::fmt::Display for CreateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -314,6 +323,15 @@ impl From for CreateUsersWithListInputError { } } +impl From for ApiCallError { + fn from(error: CreateUsersWithListInputError) -> Self { + match error { + CreateUsersWithListInputError::Unexpected(error) => Self::from_api_error("create_users_with_list_input", error), + CreateUsersWithListInputError::Transport(error) => Self::from_runtime_error("create_users_with_list_input", error), + } + } +} + impl std::fmt::Display for CreateUsersWithListInputError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -353,6 +371,16 @@ impl From for LoginUserError { } } +impl From for ApiCallError { + fn from(error: LoginUserError) -> Self { + match error { + LoginUserError::BadRequest(error) => Self::from_api_error("login_user", error), + LoginUserError::Unexpected(error) => Self::from_api_error("login_user", error), + LoginUserError::Transport(error) => Self::from_runtime_error("login_user", error), + } + } +} + impl std::fmt::Display for LoginUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -392,6 +420,15 @@ impl From for LogoutUserError { } } +impl From for ApiCallError { + fn from(error: LogoutUserError) -> Self { + match error { + LogoutUserError::Unexpected(error) => Self::from_api_error("logout_user", error), + LogoutUserError::Transport(error) => Self::from_runtime_error("logout_user", error), + } + } +} + impl std::fmt::Display for LogoutUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -433,6 +470,17 @@ impl From for GetUserByNameError { } } +impl From for ApiCallError { + fn from(error: GetUserByNameError) -> Self { + match error { + GetUserByNameError::BadRequest(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::NotFound(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Unexpected(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Transport(error) => Self::from_runtime_error("get_user_by_name", error), + } + } +} + impl std::fmt::Display for GetUserByNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -475,6 +523,17 @@ impl From for UpdateUserError { } } +impl From for ApiCallError { + fn from(error: UpdateUserError) -> Self { + match error { + UpdateUserError::BadRequest(error) => Self::from_api_error("update_user", error), + UpdateUserError::NotFound(error) => Self::from_api_error("update_user", error), + UpdateUserError::Unexpected(error) => Self::from_api_error("update_user", error), + UpdateUserError::Transport(error) => Self::from_runtime_error("update_user", error), + } + } +} + impl std::fmt::Display for UpdateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -517,6 +576,17 @@ impl From for DeleteUserError { } } +impl From for ApiCallError { + fn from(error: DeleteUserError) -> Self { + match error { + DeleteUserError::BadRequest(error) => Self::from_api_error("delete_user", error), + DeleteUserError::NotFound(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Unexpected(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Transport(error) => Self::from_runtime_error("delete_user", error), + } + } +} + impl std::fmt::Display for DeleteUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/petstore/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/petstore/src/runtime/error.rs.golden index c2ad4e4d..a6e693e2 100644 --- a/tests/golden/rust/rust-reqwest/petstore/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/petstore/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/query-param-enum/src/apis/items.rs.golden b/tests/golden/rust/rust-reqwest/query-param-enum/src/apis/items.rs.golden index 175559ae..552ba421 100644 --- a/tests/golden/rust/rust-reqwest/query-param-enum/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-reqwest/query-param-enum/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test case for query parameters that reference enum schemas use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a> { @@ -84,6 +84,16 @@ impl From for GetItemsError { } } +impl From for ApiCallError { + fn from(error: GetItemsError) -> Self { + match error { + GetItemsError::BadRequest(error) => Self::from_api_error("get_items", error), + GetItemsError::Unexpected(error) => Self::from_api_error("get_items", error), + GetItemsError::Transport(error) => Self::from_runtime_error("get_items", error), + } + } +} + impl std::fmt::Display for GetItemsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/query-param-enum/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/query-param-enum/src/runtime/error.rs.golden index 55685ecc..a45ac4a0 100644 --- a/tests/golden/rust/rust-reqwest/query-param-enum/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/query-param-enum/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/apis/default.rs.golden index 9324bcbf..efa1a250 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with all optional properties including arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/runtime/error.rs.golden index ed938b33..05f6b518 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-all-optional-properties/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/apis/default.rs.golden index 35bcd8db..0a30169b 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects with snake_case to camelCase conversion (main case from user issue) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden index 17d8d5d9..e57e34e1 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/apis/default.rs.golden index 192c6a90..5853b348 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of referenced model types with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden index 5a9d0dcd..79b0dae0 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/apis/default.rs.golden index dedb8fde..c8bf6273 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects that contain a reference property use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/runtime/error.rs.golden index 7dae00c2..f60b49c5 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-array-with-reference-property/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/apis/default.rs.golden index 4d4d7e5d..25152e1a 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects where each object has nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/runtime/error.rs.golden index ca84c511..f8b65523 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-complex-array-structure/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/apis/default.rs.golden index 103fa412..551c97b7 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for three levels of nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden index fb217c42..d581a45a 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/apis/default.rs.golden index 508d7b23..5b86b61c 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for empty array handling use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/runtime/error.rs.golden index 82d50510..9bc97b4a 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-empty-array/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/apis/default.rs.golden index 5a314ca7..1a0bc081 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline object containing an array of inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/runtime/error.rs.golden index 8b2f4a1a..034a34b2 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-inline-object-with-array/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/apis/default.rs.golden index b39f769e..177f5d30 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline objects (not arrays) with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/runtime/error.rs.golden index 0d7749e7..31ae4028 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-inline-object/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/apis/default.rs.golden index 81690d18..2ac44d7a 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with mix of simple types, arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/runtime/error.rs.golden index 97df5c69..4072f32f 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-mixed-property-types/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/apis/default.rs.golden index 3e7c9f4c..100eb019 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for nested object reference with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/runtime/error.rs.golden index fa13bfbe..d95141c4 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-nested-object-reference/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden index 26bb2a9d..fcb6f33b 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of inline objects with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden index e9d92cba..96ecd07c 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden index 722b28d8..040f8164 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of referenced model types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden index 18f0bcc9..4fc5c7d1 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/apis/default.rs.golden index a7d3ca61..c9d3cf3d 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/runtime/error.rs.golden index 3463a21b..110c58ec 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-inline-object/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden index 2dc4f7f5..ff3047fb 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional nested object reference use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden index 3baf8cfa..71e97afa 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/apis/default.rs.golden index 46bba896..3304c68b 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for arrays with primitive items (should not be affected by recursive parsing) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/runtime/error.rs.golden index 1783dc7f..267893b4 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-primitive-array/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/apis/default.rs.golden index 7e951d6a..3ea7f289 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Self-Referential Object Test — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -69,6 +69,15 @@ impl From for GetFooError { } } +impl From for ApiCallError { + fn from(error: GetFooError) -> Self { + match error { + GetFooError::Unexpected(error) => Self::from_api_error("getFoo", error), + GetFooError::Transport(error) => Self::from_runtime_error("getFoo", error), + } + } +} + impl std::fmt::Display for GetFooError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/runtime/error.rs.golden index 2200204a..d28199a1 100644 --- a/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/recursive-json-self-referential-object/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/request-body-content-types/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/request-body-content-types/src/apis/default.rs.golden index 3b1913fc..7691404a 100644 --- a/tests/golden/rust/rust-reqwest/request-body-content-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/request-body-content-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers non-JSON request body media types to pin Content-Type emission. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -256,6 +256,15 @@ impl From for PostBinaryError { } } +impl From for ApiCallError { + fn from(error: PostBinaryError) -> Self { + match error { + PostBinaryError::Unexpected(error) => Self::from_api_error("postBinary", error), + PostBinaryError::Transport(error) => Self::from_runtime_error("postBinary", error), + } + } +} + impl std::fmt::Display for PostBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -294,6 +303,15 @@ impl From for PostFormError { } } +impl From for ApiCallError { + fn from(error: PostFormError) -> Self { + match error { + PostFormError::Unexpected(error) => Self::from_api_error("postForm", error), + PostFormError::Transport(error) => Self::from_runtime_error("postForm", error), + } + } +} + impl std::fmt::Display for PostFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -332,6 +350,15 @@ impl From for PostJsonError { } } +impl From for ApiCallError { + fn from(error: PostJsonError) -> Self { + match error { + PostJsonError::Unexpected(error) => Self::from_api_error("postJson", error), + PostJsonError::Transport(error) => Self::from_runtime_error("postJson", error), + } + } +} + impl std::fmt::Display for PostJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -370,6 +397,15 @@ impl From for PostJsonOrXmlError { } } +impl From for ApiCallError { + fn from(error: PostJsonOrXmlError) -> Self { + match error { + PostJsonOrXmlError::Unexpected(error) => Self::from_api_error("postJsonOrXml", error), + PostJsonOrXmlError::Transport(error) => Self::from_runtime_error("postJsonOrXml", error), + } + } +} + impl std::fmt::Display for PostJsonOrXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -408,6 +444,15 @@ impl From for PatchMergeJsonError { } } +impl From for ApiCallError { + fn from(error: PatchMergeJsonError) -> Self { + match error { + PatchMergeJsonError::Unexpected(error) => Self::from_api_error("patchMergeJson", error), + PatchMergeJsonError::Transport(error) => Self::from_runtime_error("patchMergeJson", error), + } + } +} + impl std::fmt::Display for PatchMergeJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -446,6 +491,15 @@ impl From for PostTextError { } } +impl From for ApiCallError { + fn from(error: PostTextError) -> Self { + match error { + PostTextError::Unexpected(error) => Self::from_api_error("postText", error), + PostTextError::Transport(error) => Self::from_runtime_error("postText", error), + } + } +} + impl std::fmt::Display for PostTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -484,6 +538,15 @@ impl From for PostXmlError { } } +impl From for ApiCallError { + fn from(error: PostXmlError) -> Self { + match error { + PostXmlError::Unexpected(error) => Self::from_api_error("postXml", error), + PostXmlError::Transport(error) => Self::from_runtime_error("postXml", error), + } + } +} + impl std::fmt::Display for PostXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -523,6 +586,15 @@ impl From for GetXmlResponseError { } } +impl From for ApiCallError { + fn from(error: GetXmlResponseError) -> Self { + match error { + GetXmlResponseError::Unexpected(error) => Self::from_api_error("getXmlResponse", error), + GetXmlResponseError::Transport(error) => Self::from_runtime_error("getXmlResponse", error), + } + } +} + impl std::fmt::Display for GetXmlResponseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/request-body-content-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/request-body-content-types/src/runtime/error.rs.golden index c09e84e8..41474c3b 100644 --- a/tests/golden/rust/rust-reqwest/request-body-content-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/request-body-content-types/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/apis/widget.rs.golden b/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/apis/widget.rs.golden index 97e3b6f7..858959bd 100644 --- a/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Default And Exact Response API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a> { @@ -70,6 +70,16 @@ impl From for GetWidgetError { } } +impl From for ApiCallError { + fn from(error: GetWidgetError) -> Self { + match error { + GetWidgetError::Default(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Unexpected(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Transport(error) => Self::from_runtime_error("getWidget", error), + } + } +} + impl std::fmt::Display for GetWidgetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/runtime/error.rs.golden index eab19584..d9edaa35 100644 --- a/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-default-and-exact/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/response-body-fallback/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/response-body-fallback/src/apis/default.rs.golden index dac294c6..35c1ed90 100644 --- a/tests/golden/rust/rust-reqwest/response-body-fallback/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-fallback/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Response Fallback Test API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -101,6 +101,16 @@ impl From for GetFallbackNoBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackNoBodyError) -> Self { + match error { + GetFallbackNoBodyError::BadRequest(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Unexpected(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Transport(error) => Self::from_runtime_error("get_fallback_no_body", error), + } + } +} + impl std::fmt::Display for GetFallbackNoBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -142,6 +152,16 @@ impl From for GetFallbackWithBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackWithBodyError) -> Self { + match error { + GetFallbackWithBodyError::NotFound(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Unexpected(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Transport(error) => Self::from_runtime_error("get_fallback_with_body", error), + } + } +} + impl std::fmt::Display for GetFallbackWithBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/response-body-fallback/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/response-body-fallback/src/runtime/error.rs.golden index 266fa3eb..38d5d509 100644 --- a/tests/golden/rust/rust-reqwest/response-body-fallback/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-fallback/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/apis/widget.rs.golden b/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/apis/widget.rs.golden index a5aa4ae4..7f5a1549 100644 --- a/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Multi Status API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a> { @@ -78,6 +78,16 @@ impl From for ListWidgetsError { } } +impl From for ApiCallError { + fn from(error: ListWidgetsError) -> Self { + match error { + ListWidgetsError::NotFound(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Unexpected(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Transport(error) => Self::from_runtime_error("listWidgets", error), + } + } +} + impl std::fmt::Display for ListWidgetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/runtime/error.rs.golden index e9175dc1..ae1afe52 100644 --- a/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-multi-status-responses/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/apis/foo.rs.golden b/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/apis/foo.rs.golden index 71ff0980..c6b6d013 100644 --- a/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/apis/foo.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/apis/foo.rs.golden @@ -4,7 +4,7 @@ // No Response Body API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Foo" tag. pub struct FooApi<'a> { @@ -78,6 +78,17 @@ impl From for UpdateFooBarError { } } +impl From for ApiCallError { + fn from(error: UpdateFooBarError) -> Self { + match error { + UpdateFooBarError::BadRequest(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::InternalServerError(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Unexpected(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Transport(error) => Self::from_runtime_error("updateFooBar", error), + } + } +} + impl std::fmt::Display for UpdateFooBarError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/runtime/error.rs.golden index af8a0700..093578f2 100644 --- a/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/response-body-no-response-body/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/server-object/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/server-object/src/apis/default.rs.golden index 01cc6eeb..3fbb4d78 100644 --- a/tests/golden/rust/rust-reqwest/server-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/server-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // This is a test API specification that includes various server configurations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -108,6 +108,15 @@ impl From for GetAllUsersError { } } +impl From for ApiCallError { + fn from(error: GetAllUsersError) -> Self { + match error { + GetAllUsersError::Unexpected(error) => Self::from_api_error("getAllUsers", error), + GetAllUsersError::Transport(error) => Self::from_runtime_error("getAllUsers", error), + } + } +} + impl std::fmt::Display for GetAllUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -148,6 +157,16 @@ impl From for GetUserByIdError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdError) -> Self { + match error { + GetUserByIdError::NotFound(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Unexpected(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Transport(error) => Self::from_runtime_error("getUserById", error), + } + } +} + impl std::fmt::Display for GetUserByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/server-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/server-object/src/runtime/error.rs.golden index 22340a33..3a61c25c 100644 --- a/tests/golden/rust/rust-reqwest/server-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/server-object/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/server-path-prefix/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/server-path-prefix/src/apis/default.rs.golden index 44825fc6..2e86d3b8 100644 --- a/tests/golden/rust/rust-reqwest/server-path-prefix/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/server-path-prefix/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for server URL path prefix stripping in operation paths. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -115,6 +115,15 @@ impl From for HealthCheckError { } } +impl From for ApiCallError { + fn from(error: HealthCheckError) -> Self { + match error { + HealthCheckError::Unexpected(error) => Self::from_api_error("health_check", error), + HealthCheckError::Transport(error) => Self::from_runtime_error("health_check", error), + } + } +} + impl std::fmt::Display for HealthCheckError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -153,6 +162,15 @@ impl From for ListUsersError { } } +impl From for ApiCallError { + fn from(error: ListUsersError) -> Self { + match error { + ListUsersError::Unexpected(error) => Self::from_api_error("list_users", error), + ListUsersError::Transport(error) => Self::from_runtime_error("list_users", error), + } + } +} + impl std::fmt::Display for ListUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -191,6 +209,15 @@ impl From for GetUserError { } } +impl From for ApiCallError { + fn from(error: GetUserError) -> Self { + match error { + GetUserError::Unexpected(error) => Self::from_api_error("get_user", error), + GetUserError::Transport(error) => Self::from_runtime_error("get_user", error), + } + } +} + impl std::fmt::Display for GetUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/server-path-prefix/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/server-path-prefix/src/runtime/error.rs.golden index 2d40d97e..7b3e8d93 100644 --- a/tests/golden/rust/rust-reqwest/server-path-prefix/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/server-path-prefix/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/apis/default.rs.golden index eb6ac5b3..e971004c 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for complex union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestComplexUnionError { } } +impl From for ApiCallError { + fn from(error: TestComplexUnionError) -> Self { + match error { + TestComplexUnionError::Unexpected(error) => Self::from_api_error("test_complex_union", error), + TestComplexUnionError::Transport(error) => Self::from_runtime_error("test_complex_union", error), + } + } +} + impl std::fmt::Display for TestComplexUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/runtime/error.rs.golden index 43c41bf0..88de117a 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-complex-union/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden index 2e4b182e..b1190b5a 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden @@ -14,7 +14,7 @@ // "EventKindUnspecified" instead of generic "Kind". use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -81,6 +81,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden index 2d539d61..21a9c332 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden @@ -113,3 +113,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden index bd723a7b..ff507d9d 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // See: https://www.openapis.org/understanding-openapi/specification/models/ use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -74,6 +74,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden index c826a0ef..2e5cc67e 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden @@ -106,3 +106,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden index 4ebac499..86f91ee0 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // 100-char render width. Ensures PEP 695 parenthesization is correct. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -74,6 +74,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden index 7facd805..8a3ede0e 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden @@ -106,3 +106,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden index c92b0649..1cd5f350 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden @@ -10,7 +10,7 @@ // is a unit variant (no fields) and others carry data from referenced schemas. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -77,6 +77,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden index 238154f0..726e5b04 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden @@ -109,3 +109,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden index 8c2060cd..5d9d01c9 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden @@ -8,7 +8,7 @@ // the same Kind.ts file causing conflicts. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -105,6 +105,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -144,6 +153,15 @@ impl From for CreateVolumeError { } } +impl From for ApiCallError { + fn from(error: CreateVolumeError) -> Self { + match error { + CreateVolumeError::Unexpected(error) => Self::from_api_error("create_volume", error), + CreateVolumeError::Transport(error) => Self::from_runtime_error("create_volume", error), + } + } +} + impl std::fmt::Display for CreateVolumeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden index 9826ad16..84a4f596 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden @@ -107,3 +107,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden index dc361538..4d081c56 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // This is similar to the ContainerImage pattern in the infiron API. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -73,6 +73,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden index 6fa49507..08916819 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/apis/default.rs.golden index 636149d6..3e84fbbe 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestIntersectionError { } } +impl From for ApiCallError { + fn from(error: TestIntersectionError) -> Self { + match error { + TestIntersectionError::Unexpected(error) => Self::from_api_error("test_intersection", error), + TestIntersectionError::Transport(error) => Self::from_runtime_error("test_intersection", error), + } + } +} + impl std::fmt::Display for TestIntersectionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/runtime/error.rs.golden index ea377f1f..45f8a087 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-intersection-allof/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden index 856eba42..b18ce3fe 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types with nullable reference properties use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -102,6 +102,15 @@ impl From for CreateTestError { } } +impl From for ApiCallError { + fn from(error: CreateTestError) -> Self { + match error { + CreateTestError::Unexpected(error) => Self::from_api_error("create_test", error), + CreateTestError::Transport(error) => Self::from_runtime_error("create_test", error), + } + } +} + impl std::fmt::Display for CreateTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -141,6 +150,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("get_test", error), + GetTestError::Transport(error) => Self::from_runtime_error("get_test", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden index 91e0199b..e66f0af4 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/apis/default.rs.golden index 77872b24..d5845807 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that reference other union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestNestedUnionError { } } +impl From for ApiCallError { + fn from(error: TestNestedUnionError) -> Self { + match error { + TestNestedUnionError::Unexpected(error) => Self::from_api_error("test_nested_union", error), + TestNestedUnionError::Transport(error) => Self::from_runtime_error("test_nested_union", error), + } + } +} + impl std::fmt::Display for TestNestedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/runtime/error.rs.golden index b4723f0c..6eee8ad3 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-nested-union/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/apis/default.rs.golden index 0806b5ad..ba1bad29 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for simple type aliases (not unions or intersections) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestSimpleAliasError { } } +impl From for ApiCallError { + fn from(error: TestSimpleAliasError) -> Self { + match error { + TestSimpleAliasError::Unexpected(error) => Self::from_api_error("test_simple_alias", error), + TestSimpleAliasError::Transport(error) => Self::from_runtime_error("test_simple_alias", error), + } + } +} + impl std::fmt::Display for TestSimpleAliasError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/runtime/error.rs.golden index 5715a45c..f516f829 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-simple-type-alias/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/apis/default.rs.golden index a64f746a..74a59507 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with both interfaces and primitives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestMixedUnionError { } } +impl From for ApiCallError { + fn from(error: TestMixedUnionError) -> Self { + match error { + TestMixedUnionError::Unexpected(error) => Self::from_api_error("test_mixed_union", error), + TestMixedUnionError::Transport(error) => Self::from_runtime_error("test_mixed_union", error), + } + } +} + impl std::fmt::Display for TestMixedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/runtime/error.rs.golden index 63da1dcf..e288378e 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-mixed/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/apis/default.rs.golden index d762c875..ec468806 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that include the any type use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestUnionWithAnyError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithAnyError) -> Self { + match error { + TestUnionWithAnyError::Unexpected(error) => Self::from_api_error("test_union_with_any", error), + TestUnionWithAnyError::Transport(error) => Self::from_runtime_error("test_union_with_any", error), + } + } +} + impl std::fmt::Display for TestUnionWithAnyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/runtime/error.rs.golden index 5712e27d..45c71e1c 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-any/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/apis/default.rs.golden index 5e7375f9..f43e5e6b 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types (oneOf) with inline object schemas that should generate named interfaces use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestUnionWithInlineObjectsError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithInlineObjectsError) -> Self { + match error { + TestUnionWithInlineObjectsError::Unexpected(error) => Self::from_api_error("test_union_with_inline_objects", error), + TestUnionWithInlineObjectsError::Transport(error) => Self::from_runtime_error("test_union_with_inline_objects", error), + } + } +} + impl std::fmt::Display for TestUnionWithInlineObjectsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden index 40e34525..32ad3d14 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/apis/default.rs.golden index 1de19a44..446e57ff 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for oneOf/anyOf union types with interface members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestUnionError { } } +impl From for ApiCallError { + fn from(error: TestUnionError) -> Self { + match error { + TestUnionError::Unexpected(error) => Self::from_api_error("test_union", error), + TestUnionError::Transport(error) => Self::from_runtime_error("test_union", error), + } + } +} + impl std::fmt::Display for TestUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/runtime/error.rs.golden index 33a2ccc2..11902526 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-interfaces/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/apis/default.rs.golden index 14630496..9cca7e85 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with primitive members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for TestUnionPrimitivesError { } } +impl From for ApiCallError { + fn from(error: TestUnionPrimitivesError) -> Self { + match error { + TestUnionPrimitivesError::Unexpected(error) => Self::from_api_error("test_union_primitives", error), + TestUnionPrimitivesError::Transport(error) => Self::from_runtime_error("test_union_primitives", error), + } + } +} + impl std::fmt::Display for TestUnionPrimitivesError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/runtime/error.rs.golden index 0994ea52..f0fb1614 100644 --- a/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/type-aliases-union-with-primitives/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/typed-error-responses/src/apis/resources.rs.golden b/tests/golden/rust/rust-reqwest/typed-error-responses/src/apis/resources.rs.golden index 4d1b5443..16609586 100644 --- a/tests/golden/rust/rust-reqwest/typed-error-responses/src/apis/resources.rs.golden +++ b/tests/golden/rust/rust-reqwest/typed-error-responses/src/apis/resources.rs.golden @@ -4,7 +4,7 @@ // Typed Error Responses API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Resources" tag. pub struct ResourcesApi<'a> { @@ -170,6 +170,16 @@ impl From for CheckNoSuccessBodyError { } } +impl From for ApiCallError { + fn from(error: CheckNoSuccessBodyError) -> Self { + match error { + CheckNoSuccessBodyError::ServiceUnavailable(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Unexpected(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Transport(error) => Self::from_runtime_error("checkNoSuccessBody", error), + } + } +} + impl CheckNoSuccessBodyError { fn response_headers(&self) -> Option<&reqwest::header::HeaderMap> { match self { @@ -246,6 +256,23 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::BadRequest(error) => Self::from_api_error("createResource", error), + CreateResourceError::Conflict(error) => Self::from_api_error("createResource", error), + CreateResourceError::Validation(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status423(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status424(error) => Self::from_api_error("createResource", error), + CreateResourceError::TooManyRequests(error) => Self::from_api_error("createResource", error), + CreateResourceError::ServerError(error) => Self::from_api_error("createResource", error), + CreateResourceError::Default(error) => Self::from_api_error("createResource", error), + CreateResourceError::Unexpected(error) => Self::from_api_error("createResource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("createResource", error), + } + } +} + impl CreateResourceError { fn response_headers(&self) -> Option<&reqwest::header::HeaderMap> { match self { @@ -336,6 +363,16 @@ impl From for CreateResourceWithWildcardSuccessError { } } +impl From for ApiCallError { + fn from(error: CreateResourceWithWildcardSuccessError) -> Self { + match error { + CreateResourceWithWildcardSuccessError::BadRequest(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Unexpected(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Transport(error) => Self::from_runtime_error("createResourceWithWildcardSuccess", error), + } + } +} + impl std::fmt::Display for CreateResourceWithWildcardSuccessError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/typed-error-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/typed-error-responses/src/runtime/error.rs.golden index e05104ad..bcd50b31 100644 --- a/tests/golden/rust/rust-reqwest/typed-error-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/typed-error-responses/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/utoipa-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-mixed/src/apis/default.rs.golden index 1fb1c260..7e8e5691 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture covering all schema kinds with utoipa enabled use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -169,6 +169,15 @@ impl From for CreateFeedError { } } +impl From for ApiCallError { + fn from(error: CreateFeedError) -> Self { + match error { + CreateFeedError::Unexpected(error) => Self::from_api_error("create_feed", error), + CreateFeedError::Transport(error) => Self::from_runtime_error("create_feed", error), + } + } +} + impl std::fmt::Display for CreateFeedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -208,6 +217,15 @@ impl From for CreatePaymentError { } } +impl From for ApiCallError { + fn from(error: CreatePaymentError) -> Self { + match error { + CreatePaymentError::Unexpected(error) => Self::from_api_error("create_payment", error), + CreatePaymentError::Transport(error) => Self::from_runtime_error("create_payment", error), + } + } +} + impl std::fmt::Display for CreatePaymentError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -247,6 +265,15 @@ impl From for ListPetsError { } } +impl From for ApiCallError { + fn from(error: ListPetsError) -> Self { + match error { + ListPetsError::Unexpected(error) => Self::from_api_error("list_pets", error), + ListPetsError::Transport(error) => Self::from_runtime_error("list_pets", error), + } + } +} + impl std::fmt::Display for ListPetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -286,6 +313,15 @@ impl From for CreatePetError { } } +impl From for ApiCallError { + fn from(error: CreatePetError) -> Self { + match error { + CreatePetError::Unexpected(error) => Self::from_api_error("create_pet", error), + CreatePetError::Transport(error) => Self::from_runtime_error("create_pet", error), + } + } +} + impl std::fmt::Display for CreatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/utoipa-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-mixed/src/runtime/error.rs.golden index 1e86bf75..1120d985 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-mixed/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/apis/default.rs.golden index 7081f693..1374b922 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for untagged union manual utoipa impl generation use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/runtime/error.rs.golden index 620574da..02afd684 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-untagged-union/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/apis/default.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/apis/default.rs.golden index b3a1661f..b41a23bd 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test that utoipa::ToSchema is additive alongside user extra_derives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for CreateItemError { } } +impl From for ApiCallError { + fn from(error: CreateItemError) -> Self { + match error { + CreateItemError::Unexpected(error) => Self::from_api_error("create_item", error), + CreateItemError::Transport(error) => Self::from_runtime_error("create_item", error), + } + } +} + impl std::fmt::Display for CreateItemError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/runtime/error.rs.golden b/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/runtime/error.rs.golden index f6421cac..8f5fc1e5 100644 --- a/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-reqwest/utoipa-with-extra-derives/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: reqwest::header::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&reqwest::header::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/additional-properties/src/apis/additional_properties.rs.golden b/tests/golden/rust/rust-ureq/additional-properties/src/apis/additional_properties.rs.golden index 91f258cf..46a9e443 100644 --- a/tests/golden/rust/rust-ureq/additional-properties/src/apis/additional_properties.rs.golden +++ b/tests/golden/rust/rust-ureq/additional-properties/src/apis/additional_properties.rs.golden @@ -5,7 +5,7 @@ // API demonstrating OpenAPI additionalProperties with multiple levels of structs (RootLevel -> MiddleLevel -> LeafValue), each with HashMap fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "additional-properties" tag. pub struct AdditionalPropertiesApi<'a> { @@ -142,6 +142,16 @@ impl From for PostLeafError { } } +impl From for ApiCallError { + fn from(error: PostLeafError) -> Self { + match error { + PostLeafError::BadRequest(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Unexpected(error) => Self::from_api_error("post_leaf", error), + PostLeafError::Transport(error) => Self::from_runtime_error("post_leaf", error), + } + } +} + impl std::fmt::Display for PostLeafError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -183,6 +193,16 @@ impl From for PostMiddleError { } } +impl From for ApiCallError { + fn from(error: PostMiddleError) -> Self { + match error { + PostMiddleError::BadRequest(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Unexpected(error) => Self::from_api_error("post_middle", error), + PostMiddleError::Transport(error) => Self::from_runtime_error("post_middle", error), + } + } +} + impl std::fmt::Display for PostMiddleError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -224,6 +244,16 @@ impl From for PostRootError { } } +impl From for ApiCallError { + fn from(error: PostRootError) -> Self { + match error { + PostRootError::BadRequest(error) => Self::from_api_error("post_root", error), + PostRootError::Unexpected(error) => Self::from_api_error("post_root", error), + PostRootError::Transport(error) => Self::from_runtime_error("post_root", error), + } + } +} + impl std::fmt::Display for PostRootError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/additional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/additional-properties/src/runtime/error.rs.golden index 814da699..47f6fbc3 100644 --- a/tests/golden/rust/rust-ureq/additional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/additional-properties/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/apis/transfer.rs.golden b/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/apis/transfer.rs.golden index fbd21642..7a1b9c56 100644 --- a/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart upload and octet-stream download. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -126,6 +126,16 @@ impl From for DownloadAssetError { } } +impl From for ApiCallError { + fn from(error: DownloadAssetError) -> Self { + match error { + DownloadAssetError::NotFound(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Unexpected(error) => Self::from_api_error("download_asset", error), + DownloadAssetError::Transport(error) => Self::from_runtime_error("download_asset", error), + } + } +} + impl std::fmt::Display for DownloadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -167,6 +177,16 @@ impl From for UploadAssetError { } } +impl From for ApiCallError { + fn from(error: UploadAssetError) -> Self { + match error { + UploadAssetError::BadRequest(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Unexpected(error) => Self::from_api_error("upload_asset", error), + UploadAssetError::Transport(error) => Self::from_runtime_error("upload_asset", error), + } + } +} + impl std::fmt::Display for UploadAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/runtime/error.rs.golden index 871ca747..3935b1f0 100644 --- a/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/binary-transfer-media-types/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/comprehensive-schemas/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/comprehensive-schemas/src/apis/default.rs.golden index 4ab1c2db..e46ea4d5 100644 --- a/tests/golden/rust/rust-ureq/comprehensive-schemas/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/comprehensive-schemas/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Comprehensive test for all OpenAPI v3.1.2 schema types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/comprehensive-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/comprehensive-schemas/src/runtime/error.rs.golden index 2ee07b2b..9a4f74b0 100644 --- a/tests/golden/rust/rust-ureq/comprehensive-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/comprehensive-schemas/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/delete-with-response-schema/src/apis/test_resource.rs.golden b/tests/golden/rust/rust-ureq/delete-with-response-schema/src/apis/test_resource.rs.golden index 97a51c2c..467d7ba8 100644 --- a/tests/golden/rust/rust-ureq/delete-with-response-schema/src/apis/test_resource.rs.golden +++ b/tests/golden/rust/rust-ureq/delete-with-response-schema/src/apis/test_resource.rs.golden @@ -5,7 +5,7 @@ // Test fixture for DELETE operations with JSON response schemas and type alias request bodies use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-resource" tag. pub struct TestResourceApi<'a> { @@ -109,6 +109,15 @@ impl From for CreateTestResourceError { } } +impl From for ApiCallError { + fn from(error: CreateTestResourceError) -> Self { + match error { + CreateTestResourceError::Unexpected(error) => Self::from_api_error("create_test_resource", error), + CreateTestResourceError::Transport(error) => Self::from_runtime_error("create_test_resource", error), + } + } +} + impl std::fmt::Display for CreateTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -150,6 +159,17 @@ impl From for DeleteTestResourceError { } } +impl From for ApiCallError { + fn from(error: DeleteTestResourceError) -> Self { + match error { + DeleteTestResourceError::ClientError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::ServerError(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Unexpected(error) => Self::from_api_error("delete_test_resource", error), + DeleteTestResourceError::Transport(error) => Self::from_runtime_error("delete_test_resource", error), + } + } +} + impl std::fmt::Display for DeleteTestResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/delete-with-response-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/delete-with-response-schema/src/runtime/error.rs.golden index 7110d274..54495d83 100644 --- a/tests/golden/rust/rust-ureq/delete-with-response-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/delete-with-response-schema/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/items.rs.golden b/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/items.rs.golden index 1ee7de00..48ea7c9d 100644 --- a/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a> { @@ -82,6 +82,16 @@ impl From for CreateItemWithBodyConflictError { } } +impl From for ApiCallError { + fn from(error: CreateItemWithBodyConflictError) -> Self { + match error { + CreateItemWithBodyConflictError::BadRequest(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Unexpected(error) => Self::from_api_error("create_item_with_body_conflict", error), + CreateItemWithBodyConflictError::Transport(error) => Self::from_runtime_error("create_item_with_body_conflict", error), + } + } +} + impl std::fmt::Display for CreateItemWithBodyConflictError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/users.rs.golden b/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/users.rs.golden index 3321a906..e5e47bed 100644 --- a/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/users.rs.golden +++ b/tests/golden/rust/rust-ureq/duplicate-param-names/src/apis/users.rs.golden @@ -5,7 +5,7 @@ // Test API with duplicate parameter names across different locations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "users" tag. pub struct UsersApi<'a> { @@ -90,6 +90,17 @@ impl From for GetUserByIdDuplicateError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdDuplicateError) -> Self { + match error { + GetUserByIdDuplicateError::BadRequest(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::NotFound(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Unexpected(error) => Self::from_api_error("get_user_by_id_duplicate", error), + GetUserByIdDuplicateError::Transport(error) => Self::from_runtime_error("get_user_by_id_duplicate", error), + } + } +} + impl std::fmt::Display for GetUserByIdDuplicateError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/duplicate-param-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/duplicate-param-names/src/runtime/error.rs.golden index 3f176acb..a902529b 100644 --- a/tests/golden/rust/rust-ureq/duplicate-param-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/duplicate-param-names/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/enum-repr/src/apis/enum_repr.rs.golden b/tests/golden/rust/rust-ureq/enum-repr/src/apis/enum_repr.rs.golden index 78c12bce..b11193ed 100644 --- a/tests/golden/rust/rust-ureq/enum-repr/src/apis/enum_repr.rs.golden +++ b/tests/golden/rust/rust-ureq/enum-repr/src/apis/enum_repr.rs.golden @@ -5,7 +5,7 @@ // This API demonstrates all 4 kinds of enum representation types: Externally Tagged, Internally Tagged, Adjacently Tagged, and Untagged use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "enum-repr" tag. pub struct EnumReprApi<'a> { @@ -208,6 +208,16 @@ impl From for HandleAdjacentlyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleAdjacentlyTaggedError) -> Self { + match error { + HandleAdjacentlyTaggedError::BadRequest(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Unexpected(error) => Self::from_api_error("handle_adjacently_tagged", error), + HandleAdjacentlyTaggedError::Transport(error) => Self::from_runtime_error("handle_adjacently_tagged", error), + } + } +} + impl std::fmt::Display for HandleAdjacentlyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -249,6 +259,16 @@ impl From for HandleExternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleExternallyTaggedError) -> Self { + match error { + HandleExternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_externally_tagged", error), + HandleExternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_externally_tagged", error), + } + } +} + impl std::fmt::Display for HandleExternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -290,6 +310,16 @@ impl From for HandleInternallyTaggedError { } } +impl From for ApiCallError { + fn from(error: HandleInternallyTaggedError) -> Self { + match error { + HandleInternallyTaggedError::BadRequest(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Unexpected(error) => Self::from_api_error("handle_internally_tagged", error), + HandleInternallyTaggedError::Transport(error) => Self::from_runtime_error("handle_internally_tagged", error), + } + } +} + impl std::fmt::Display for HandleInternallyTaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -331,6 +361,16 @@ impl From for HandleMixedError { } } +impl From for ApiCallError { + fn from(error: HandleMixedError) -> Self { + match error { + HandleMixedError::BadRequest(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Unexpected(error) => Self::from_api_error("handle_mixed", error), + HandleMixedError::Transport(error) => Self::from_runtime_error("handle_mixed", error), + } + } +} + impl std::fmt::Display for HandleMixedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -372,6 +412,16 @@ impl From for HandleUntaggedError { } } +impl From for ApiCallError { + fn from(error: HandleUntaggedError) -> Self { + match error { + HandleUntaggedError::BadRequest(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Unexpected(error) => Self::from_api_error("handle_untagged", error), + HandleUntaggedError::Transport(error) => Self::from_runtime_error("handle_untagged", error), + } + } +} + impl std::fmt::Display for HandleUntaggedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/enum-repr/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/enum-repr/src/runtime/error.rs.golden index 41e1f254..b46f815d 100644 --- a/tests/golden/rust/rust-ureq/enum-repr/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/enum-repr/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/media-type-selection/src/apis/media.rs.golden b/tests/golden/rust/rust-ureq/media-type-selection/src/apis/media.rs.golden index f6c82467..509ecbef 100644 --- a/tests/golden/rust/rust-ureq/media-type-selection/src/apis/media.rs.golden +++ b/tests/golden/rust/rust-ureq/media-type-selection/src/apis/media.rs.golden @@ -5,7 +5,7 @@ // Covers normalized media-type selection for requests and responses. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "media" tag. pub struct MediaApi<'a> { @@ -214,6 +214,15 @@ impl From for SendJsonPreferredError { } } +impl From for ApiCallError { + fn from(error: SendJsonPreferredError) -> Self { + match error { + SendJsonPreferredError::Unexpected(error) => Self::from_api_error("send_json_preferred", error), + SendJsonPreferredError::Transport(error) => Self::from_runtime_error("send_json_preferred", error), + } + } +} + impl std::fmt::Display for SendJsonPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -252,6 +261,15 @@ impl From for SendParameterizedMultipartError { } } +impl From for ApiCallError { + fn from(error: SendParameterizedMultipartError) -> Self { + match error { + SendParameterizedMultipartError::Unexpected(error) => Self::from_api_error("send_parameterized_multipart", error), + SendParameterizedMultipartError::Transport(error) => Self::from_runtime_error("send_parameterized_multipart", error), + } + } +} + impl std::fmt::Display for SendParameterizedMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -290,6 +308,15 @@ impl From for SendVendorJsonError { } } +impl From for ApiCallError { + fn from(error: SendVendorJsonError) -> Self { + match error { + SendVendorJsonError::Unexpected(error) => Self::from_api_error("send_vendor_json", error), + SendVendorJsonError::Transport(error) => Self::from_runtime_error("send_vendor_json", error), + } + } +} + impl std::fmt::Display for SendVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -329,6 +356,15 @@ impl From for GetOctetPreferredError { } } +impl From for ApiCallError { + fn from(error: GetOctetPreferredError) -> Self { + match error { + GetOctetPreferredError::Unexpected(error) => Self::from_api_error("get_octet_preferred", error), + GetOctetPreferredError::Transport(error) => Self::from_runtime_error("get_octet_preferred", error), + } + } +} + impl std::fmt::Display for GetOctetPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -368,6 +404,15 @@ impl From for GetTextPreferredError { } } +impl From for ApiCallError { + fn from(error: GetTextPreferredError) -> Self { + match error { + GetTextPreferredError::Unexpected(error) => Self::from_api_error("get_text_preferred", error), + GetTextPreferredError::Transport(error) => Self::from_runtime_error("get_text_preferred", error), + } + } +} + impl std::fmt::Display for GetTextPreferredError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -407,6 +452,15 @@ impl From for GetVendorJsonError { } } +impl From for ApiCallError { + fn from(error: GetVendorJsonError) -> Self { + match error { + GetVendorJsonError::Unexpected(error) => Self::from_api_error("get_vendor_json", error), + GetVendorJsonError::Transport(error) => Self::from_runtime_error("get_vendor_json", error), + } + } +} + impl std::fmt::Display for GetVendorJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/media-type-selection/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/media-type-selection/src/runtime/error.rs.golden index 2398107e..168d4829 100644 --- a/tests/golden/rust/rust-ureq/media-type-selection/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/media-type-selection/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/minimal/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/minimal/src/apis/default.rs.golden index 5d479be4..ec47af05 100644 --- a/tests/golden/rust/rust-ureq/minimal/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/minimal/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Minimal API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -64,6 +64,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("getTest", error), + GetTestError::Transport(error) => Self::from_runtime_error("getTest", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/minimal/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/minimal/src/runtime/error.rs.golden index d6dfb82f..79141951 100644 --- a/tests/golden/rust/rust-ureq/minimal/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/minimal/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/apis/default.rs.golden index 13c48952..db03acad 100644 --- a/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // and primitive type alias suppression. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -110,6 +110,15 @@ impl From for AcceptNodeError { } } +impl From for ApiCallError { + fn from(error: AcceptNodeError) -> Self { + match error { + AcceptNodeError::Unexpected(error) => Self::from_api_error("acceptNode", error), + AcceptNodeError::Transport(error) => Self::from_runtime_error("acceptNode", error), + } + } +} + impl std::fmt::Display for AcceptNodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -149,6 +158,15 @@ impl From for GetNodeNameError { } } +impl From for ApiCallError { + fn from(error: GetNodeNameError) -> Self { + match error { + GetNodeNameError::Unexpected(error) => Self::from_api_error("getNodeName", error), + GetNodeNameError::Transport(error) => Self::from_runtime_error("getNodeName", error), + } + } +} + impl std::fmt::Display for GetNodeNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden index 51604d02..1137d9df 100644 --- a/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multiline-docs-and-primitive-alias/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multipart-edge-cases/src/apis/multipart.rs.golden b/tests/golden/rust/rust-ureq/multipart-edge-cases/src/apis/multipart.rs.golden index fcc36c1d..ac97f304 100644 --- a/tests/golden/rust/rust-ureq/multipart-edge-cases/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-edge-cases/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers optional multipart bodies, optional parts, and text-only multipart fields. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a> { @@ -147,6 +147,15 @@ impl From for SendOptionalPartsError { } } +impl From for ApiCallError { + fn from(error: SendOptionalPartsError) -> Self { + match error { + SendOptionalPartsError::Unexpected(error) => Self::from_api_error("send_optional_parts", error), + SendOptionalPartsError::Transport(error) => Self::from_runtime_error("send_optional_parts", error), + } + } +} + impl std::fmt::Display for SendOptionalPartsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -185,6 +194,15 @@ impl From for SendTextFieldsError { } } +impl From for ApiCallError { + fn from(error: SendTextFieldsError) -> Self { + match error { + SendTextFieldsError::Unexpected(error) => Self::from_api_error("send_text_fields", error), + SendTextFieldsError::Transport(error) => Self::from_runtime_error("send_text_fields", error), + } + } +} + impl std::fmt::Display for SendTextFieldsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multipart-edge-cases/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multipart-edge-cases/src/runtime/error.rs.golden index 3ad70058..a5dac92c 100644 --- a/tests/golden/rust/rust-ureq/multipart-edge-cases/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-edge-cases/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/apis/transfer.rs.golden b/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/apis/transfer.rs.golden index b507b500..c462500a 100644 --- a/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers explicit multipart part content types. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -86,6 +86,15 @@ impl From for UploadEncodedAssetError { } } +impl From for ApiCallError { + fn from(error: UploadEncodedAssetError) -> Self { + match error { + UploadEncodedAssetError::Unexpected(error) => Self::from_api_error("upload_encoded_asset", error), + UploadEncodedAssetError::Transport(error) => Self::from_runtime_error("upload_encoded_asset", error), + } + } +} + impl std::fmt::Display for UploadEncodedAssetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/runtime/error.rs.golden index 05decd58..66ebce8b 100644 --- a/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-explicit-encoding/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/apis/multipart.rs.golden b/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/apis/multipart.rs.golden index ce2700ee..cd833394 100644 --- a/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/apis/multipart.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/apis/multipart.rs.golden @@ -5,7 +5,7 @@ // Covers multipart object parts whose wire names differ from ergonomic names. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "multipart" tag. pub struct MultipartApi<'a> { @@ -78,6 +78,15 @@ impl From for SendNestedObjectPartError { } } +impl From for ApiCallError { + fn from(error: SendNestedObjectPartError) -> Self { + match error { + SendNestedObjectPartError::Unexpected(error) => Self::from_api_error("send_nested_object_part", error), + SendNestedObjectPartError::Transport(error) => Self::from_runtime_error("send_nested_object_part", error), + } + } +} + impl std::fmt::Display for SendNestedObjectPartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/runtime/error.rs.golden index 2abd3d84..de770deb 100644 --- a/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-nested-object-parts/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/apis/transfer.rs.golden b/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/apis/transfer.rs.golden index 128b1360..b92da650 100644 --- a/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/apis/transfer.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/apis/transfer.rs.golden @@ -5,7 +5,7 @@ // Covers multipart request bodies that are not object-shaped. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "transfer" tag. pub struct TransferApi<'a> { @@ -51,6 +51,15 @@ impl From for UploadRawMultipartError { } } +impl From for ApiCallError { + fn from(error: UploadRawMultipartError) -> Self { + match error { + UploadRawMultipartError::Unexpected(error) => Self::from_api_error("upload_raw_multipart", error), + UploadRawMultipartError::Transport(error) => Self::from_runtime_error("upload_raw_multipart", error), + } + } +} + impl std::fmt::Display for UploadRawMultipartError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/runtime/error.rs.golden index 5ec86d5a..e3fd84bb 100644 --- a/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multipart-unsupported-schema/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/apis/test_api.rs.golden b/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/apis/test_api.rs.golden index 6cda3c91..62629da9 100644 --- a/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/apis/test_api.rs.golden +++ b/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/apis/test_api.rs.golden @@ -5,7 +5,7 @@ // Test API with multiple operations having similar request body schema names use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "test-api" tag. pub struct TestApiApi<'a> { @@ -100,6 +100,15 @@ impl From for CreateTypeAError { } } +impl From for ApiCallError { + fn from(error: CreateTypeAError) -> Self { + match error { + CreateTypeAError::Unexpected(error) => Self::from_api_error("create_type_a", error), + CreateTypeAError::Transport(error) => Self::from_runtime_error("create_type_a", error), + } + } +} + impl std::fmt::Display for CreateTypeAError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -139,6 +148,15 @@ impl From for CreateTypeBError { } } +impl From for ApiCallError { + fn from(error: CreateTypeBError) -> Self { + match error { + CreateTypeBError::Unexpected(error) => Self::from_api_error("create_type_b", error), + CreateTypeBError::Transport(error) => Self::from_runtime_error("create_type_b", error), + } + } +} + impl std::fmt::Display for CreateTypeBError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/runtime/error.rs.golden index b2ed35c7..c48b093d 100644 --- a/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/multiple-similar-request-schemas/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/naming-conventions/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/naming-conventions/src/apis/default.rs.golden index 4f6afa1d..7491ddbc 100644 --- a/tests/golden/rust/rust-ureq/naming-conventions/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/naming-conventions/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture to verify language property naming conventions use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -142,6 +142,15 @@ impl From for GetWithQueryParamsError { } } +impl From for ApiCallError { + fn from(error: GetWithQueryParamsError) -> Self { + match error { + GetWithQueryParamsError::Unexpected(error) => Self::from_api_error("get_with_query_params", error), + GetWithQueryParamsError::Transport(error) => Self::from_runtime_error("get_with_query_params", error), + } + } +} + impl std::fmt::Display for GetWithQueryParamsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -181,6 +190,15 @@ impl From for TestNamingConventionsError { } } +impl From for ApiCallError { + fn from(error: TestNamingConventionsError) -> Self { + match error { + TestNamingConventionsError::Unexpected(error) => Self::from_api_error("test_naming_conventions", error), + TestNamingConventionsError::Transport(error) => Self::from_runtime_error("test_naming_conventions", error), + } + } +} + impl std::fmt::Display for TestNamingConventionsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/naming-conventions/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/naming-conventions/src/runtime/error.rs.golden index 49364d3c..047fc825 100644 --- a/tests/golden/rust/rust-ureq/naming-conventions/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/naming-conventions/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/optional-request-bodies/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/optional-request-bodies/src/apis/default.rs.golden index 90294a2c..944fcc29 100644 --- a/tests/golden/rust/rust-ureq/optional-request-bodies/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/optional-request-bodies/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers optional non-multipart request bodies. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -161,6 +161,15 @@ impl From for SendOptionalBinaryError { } } +impl From for ApiCallError { + fn from(error: SendOptionalBinaryError) -> Self { + match error { + SendOptionalBinaryError::Unexpected(error) => Self::from_api_error("send_optional_binary", error), + SendOptionalBinaryError::Transport(error) => Self::from_runtime_error("send_optional_binary", error), + } + } +} + impl std::fmt::Display for SendOptionalBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -199,6 +208,15 @@ impl From for SendOptionalJsonError { } } +impl From for ApiCallError { + fn from(error: SendOptionalJsonError) -> Self { + match error { + SendOptionalJsonError::Unexpected(error) => Self::from_api_error("send_optional_json", error), + SendOptionalJsonError::Transport(error) => Self::from_runtime_error("send_optional_json", error), + } + } +} + impl std::fmt::Display for SendOptionalJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -237,6 +255,15 @@ impl From for SendOptionalTextError { } } +impl From for ApiCallError { + fn from(error: SendOptionalTextError) -> Self { + match error { + SendOptionalTextError::Unexpected(error) => Self::from_api_error("send_optional_text", error), + SendOptionalTextError::Transport(error) => Self::from_runtime_error("send_optional_text", error), + } + } +} + impl std::fmt::Display for SendOptionalTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -275,6 +302,15 @@ impl From for SendRequiredJsonError { } } +impl From for ApiCallError { + fn from(error: SendRequiredJsonError) -> Self { + match error { + SendRequiredJsonError::Unexpected(error) => Self::from_api_error("send_required_json", error), + SendRequiredJsonError::Transport(error) => Self::from_runtime_error("send_required_json", error), + } + } +} + impl std::fmt::Display for SendRequiredJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/optional-request-bodies/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/optional-request-bodies/src/runtime/error.rs.golden index 07990225..2cf88c0e 100644 --- a/tests/golden/rust/rust-ureq/optional-request-bodies/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/optional-request-bodies/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/petstore/src/apis/pet.rs.golden b/tests/golden/rust/rust-ureq/petstore/src/apis/pet.rs.golden index 5b0d1823..b50ed025 100644 --- a/tests/golden/rust/rust-ureq/petstore/src/apis/pet.rs.golden +++ b/tests/golden/rust/rust-ureq/petstore/src/apis/pet.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "pet" tag. pub struct PetApi<'a> { @@ -337,6 +337,18 @@ impl From for UpdatePetError { } } +impl From for ApiCallError { + fn from(error: UpdatePetError) -> Self { + match error { + UpdatePetError::BadRequest(error) => Self::from_api_error("update_pet", error), + UpdatePetError::NotFound(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Validation(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Unexpected(error) => Self::from_api_error("update_pet", error), + UpdatePetError::Transport(error) => Self::from_runtime_error("update_pet", error), + } + } +} + impl std::fmt::Display for UpdatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -381,6 +393,17 @@ impl From for AddPetError { } } +impl From for ApiCallError { + fn from(error: AddPetError) -> Self { + match error { + AddPetError::BadRequest(error) => Self::from_api_error("add_pet", error), + AddPetError::Validation(error) => Self::from_api_error("add_pet", error), + AddPetError::Unexpected(error) => Self::from_api_error("add_pet", error), + AddPetError::Transport(error) => Self::from_runtime_error("add_pet", error), + } + } +} + impl std::fmt::Display for AddPetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -423,6 +446,16 @@ impl From for FindPetsByStatusError { } } +impl From for ApiCallError { + fn from(error: FindPetsByStatusError) -> Self { + match error { + FindPetsByStatusError::BadRequest(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Unexpected(error) => Self::from_api_error("find_pets_by_status", error), + FindPetsByStatusError::Transport(error) => Self::from_runtime_error("find_pets_by_status", error), + } + } +} + impl std::fmt::Display for FindPetsByStatusError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -464,6 +497,16 @@ impl From for FindPetsByTagsError { } } +impl From for ApiCallError { + fn from(error: FindPetsByTagsError) -> Self { + match error { + FindPetsByTagsError::BadRequest(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Unexpected(error) => Self::from_api_error("find_pets_by_tags", error), + FindPetsByTagsError::Transport(error) => Self::from_runtime_error("find_pets_by_tags", error), + } + } +} + impl std::fmt::Display for FindPetsByTagsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -506,6 +549,17 @@ impl From for GetPetByIdError { } } +impl From for ApiCallError { + fn from(error: GetPetByIdError) -> Self { + match error { + GetPetByIdError::BadRequest(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::NotFound(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Unexpected(error) => Self::from_api_error("get_pet_by_id", error), + GetPetByIdError::Transport(error) => Self::from_runtime_error("get_pet_by_id", error), + } + } +} + impl std::fmt::Display for GetPetByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -548,6 +602,16 @@ impl From for UpdatePetWithFormError { } } +impl From for ApiCallError { + fn from(error: UpdatePetWithFormError) -> Self { + match error { + UpdatePetWithFormError::BadRequest(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Unexpected(error) => Self::from_api_error("update_pet_with_form", error), + UpdatePetWithFormError::Transport(error) => Self::from_runtime_error("update_pet_with_form", error), + } + } +} + impl std::fmt::Display for UpdatePetWithFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -588,6 +652,16 @@ impl From for DeletePetError { } } +impl From for ApiCallError { + fn from(error: DeletePetError) -> Self { + match error { + DeletePetError::BadRequest(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Unexpected(error) => Self::from_api_error("delete_pet", error), + DeletePetError::Transport(error) => Self::from_runtime_error("delete_pet", error), + } + } +} + impl std::fmt::Display for DeletePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -628,6 +702,15 @@ impl From for UploadFileError { } } +impl From for ApiCallError { + fn from(error: UploadFileError) -> Self { + match error { + UploadFileError::Unexpected(error) => Self::from_api_error("upload_file", error), + UploadFileError::Transport(error) => Self::from_runtime_error("upload_file", error), + } + } +} + impl std::fmt::Display for UploadFileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/petstore/src/apis/store.rs.golden b/tests/golden/rust/rust-ureq/petstore/src/apis/store.rs.golden index 7282ebc4..2c31a037 100644 --- a/tests/golden/rust/rust-ureq/petstore/src/apis/store.rs.golden +++ b/tests/golden/rust/rust-ureq/petstore/src/apis/store.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "store" tag. pub struct StoreApi<'a> { @@ -175,6 +175,15 @@ impl From for GetInventoryError { } } +impl From for ApiCallError { + fn from(error: GetInventoryError) -> Self { + match error { + GetInventoryError::Unexpected(error) => Self::from_api_error("get_inventory", error), + GetInventoryError::Transport(error) => Self::from_runtime_error("get_inventory", error), + } + } +} + impl std::fmt::Display for GetInventoryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -215,6 +224,16 @@ impl From for PlaceOrderError { } } +impl From for ApiCallError { + fn from(error: PlaceOrderError) -> Self { + match error { + PlaceOrderError::BadRequest(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Unexpected(error) => Self::from_api_error("place_order", error), + PlaceOrderError::Transport(error) => Self::from_runtime_error("place_order", error), + } + } +} + impl std::fmt::Display for PlaceOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -257,6 +276,17 @@ impl From for GetOrderByIdError { } } +impl From for ApiCallError { + fn from(error: GetOrderByIdError) -> Self { + match error { + GetOrderByIdError::BadRequest(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::NotFound(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Unexpected(error) => Self::from_api_error("get_order_by_id", error), + GetOrderByIdError::Transport(error) => Self::from_runtime_error("get_order_by_id", error), + } + } +} + impl std::fmt::Display for GetOrderByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -299,6 +329,17 @@ impl From for DeleteOrderError { } } +impl From for ApiCallError { + fn from(error: DeleteOrderError) -> Self { + match error { + DeleteOrderError::BadRequest(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::NotFound(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Unexpected(error) => Self::from_api_error("delete_order", error), + DeleteOrderError::Transport(error) => Self::from_runtime_error("delete_order", error), + } + } +} + impl std::fmt::Display for DeleteOrderError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/petstore/src/apis/user.rs.golden b/tests/golden/rust/rust-ureq/petstore/src/apis/user.rs.golden index 48fb0875..553f0b63 100644 --- a/tests/golden/rust/rust-ureq/petstore/src/apis/user.rs.golden +++ b/tests/golden/rust/rust-ureq/petstore/src/apis/user.rs.golden @@ -5,7 +5,7 @@ // This is a sample Pet Store Server based on the OpenAPI 3.1 specification use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "user" tag. pub struct UserApi<'a> { @@ -267,6 +267,15 @@ impl From for CreateUserError { } } +impl From for ApiCallError { + fn from(error: CreateUserError) -> Self { + match error { + CreateUserError::Unexpected(error) => Self::from_api_error("create_user", error), + CreateUserError::Transport(error) => Self::from_runtime_error("create_user", error), + } + } +} + impl std::fmt::Display for CreateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -306,6 +315,15 @@ impl From for CreateUsersWithListInputError { } } +impl From for ApiCallError { + fn from(error: CreateUsersWithListInputError) -> Self { + match error { + CreateUsersWithListInputError::Unexpected(error) => Self::from_api_error("create_users_with_list_input", error), + CreateUsersWithListInputError::Transport(error) => Self::from_runtime_error("create_users_with_list_input", error), + } + } +} + impl std::fmt::Display for CreateUsersWithListInputError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -345,6 +363,16 @@ impl From for LoginUserError { } } +impl From for ApiCallError { + fn from(error: LoginUserError) -> Self { + match error { + LoginUserError::BadRequest(error) => Self::from_api_error("login_user", error), + LoginUserError::Unexpected(error) => Self::from_api_error("login_user", error), + LoginUserError::Transport(error) => Self::from_runtime_error("login_user", error), + } + } +} + impl std::fmt::Display for LoginUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -384,6 +412,15 @@ impl From for LogoutUserError { } } +impl From for ApiCallError { + fn from(error: LogoutUserError) -> Self { + match error { + LogoutUserError::Unexpected(error) => Self::from_api_error("logout_user", error), + LogoutUserError::Transport(error) => Self::from_runtime_error("logout_user", error), + } + } +} + impl std::fmt::Display for LogoutUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -425,6 +462,17 @@ impl From for GetUserByNameError { } } +impl From for ApiCallError { + fn from(error: GetUserByNameError) -> Self { + match error { + GetUserByNameError::BadRequest(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::NotFound(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Unexpected(error) => Self::from_api_error("get_user_by_name", error), + GetUserByNameError::Transport(error) => Self::from_runtime_error("get_user_by_name", error), + } + } +} + impl std::fmt::Display for GetUserByNameError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -467,6 +515,17 @@ impl From for UpdateUserError { } } +impl From for ApiCallError { + fn from(error: UpdateUserError) -> Self { + match error { + UpdateUserError::BadRequest(error) => Self::from_api_error("update_user", error), + UpdateUserError::NotFound(error) => Self::from_api_error("update_user", error), + UpdateUserError::Unexpected(error) => Self::from_api_error("update_user", error), + UpdateUserError::Transport(error) => Self::from_runtime_error("update_user", error), + } + } +} + impl std::fmt::Display for UpdateUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -509,6 +568,17 @@ impl From for DeleteUserError { } } +impl From for ApiCallError { + fn from(error: DeleteUserError) -> Self { + match error { + DeleteUserError::BadRequest(error) => Self::from_api_error("delete_user", error), + DeleteUserError::NotFound(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Unexpected(error) => Self::from_api_error("delete_user", error), + DeleteUserError::Transport(error) => Self::from_runtime_error("delete_user", error), + } + } +} + impl std::fmt::Display for DeleteUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/petstore/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/petstore/src/runtime/error.rs.golden index f971f9db..e4908923 100644 --- a/tests/golden/rust/rust-ureq/petstore/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/petstore/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/query-param-enum/src/apis/items.rs.golden b/tests/golden/rust/rust-ureq/query-param-enum/src/apis/items.rs.golden index 97ad0a58..94a0c20c 100644 --- a/tests/golden/rust/rust-ureq/query-param-enum/src/apis/items.rs.golden +++ b/tests/golden/rust/rust-ureq/query-param-enum/src/apis/items.rs.golden @@ -5,7 +5,7 @@ // Test case for query parameters that reference enum schemas use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "items" tag. pub struct ItemsApi<'a> { @@ -79,6 +79,16 @@ impl From for GetItemsError { } } +impl From for ApiCallError { + fn from(error: GetItemsError) -> Self { + match error { + GetItemsError::BadRequest(error) => Self::from_api_error("get_items", error), + GetItemsError::Unexpected(error) => Self::from_api_error("get_items", error), + GetItemsError::Transport(error) => Self::from_runtime_error("get_items", error), + } + } +} + impl std::fmt::Display for GetItemsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/query-param-enum/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/query-param-enum/src/runtime/error.rs.golden index cfbd319c..a18cd399 100644 --- a/tests/golden/rust/rust-ureq/query-param-enum/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/query-param-enum/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/apis/default.rs.golden index 815d4740..a5ed8feb 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with all optional properties including arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/runtime/error.rs.golden index f2e94702..8e8b5f05 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-all-optional-properties/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/apis/default.rs.golden index f02eb3b1..9d0a6dd1 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects with snake_case to camelCase conversion (main case from user issue) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden index a9a4c4bb..f15cf5ec 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-of-inline-objects/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/apis/default.rs.golden index 587833e4..69459174 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of referenced model types with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden index 58bebf58..cc890db0 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-of-referenced-types/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/apis/default.rs.golden index 696fa3bd..c74d14dd 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects that contain a reference property use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/runtime/error.rs.golden index 7a2d9e2a..db6e0ad0 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-array-with-reference-property/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/apis/default.rs.golden index 61789ffa..169b7df6 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for array of inline objects where each object has nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/runtime/error.rs.golden index 175d701e..50bb3886 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-complex-array-structure/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/apis/default.rs.golden index 1fd50bc0..844d7320 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for three levels of nested inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden index 477801dd..88ed7382 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-deeply-nested-inline/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/apis/default.rs.golden index 4eb459dd..1cc265c1 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for empty array handling use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/runtime/error.rs.golden index ddc706e9..d329d525 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-empty-array/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/apis/default.rs.golden index c1d3defd..b887fff2 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline object containing an array of inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/runtime/error.rs.golden index 2657a74a..84c99914 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-inline-object-with-array/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/apis/default.rs.golden index 6bb6f3ec..8f50c3f1 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for inline objects (not arrays) with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/runtime/error.rs.golden index 4752a4c3..a5236a03 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-inline-object/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/apis/default.rs.golden index 0815fd07..caf7f968 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for object with mix of simple types, arrays, references, and inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/runtime/error.rs.golden index 3e39c06e..6402ebce 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-mixed-property-types/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/apis/default.rs.golden index a62568bd..9738dd50 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for nested object reference with recursive FromJSON calls use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/runtime/error.rs.golden index aea9f855..995d5c98 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-nested-object-reference/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden index 6ae38d6f..831922ad 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of inline objects with snake_case to camelCase conversion use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden index 47b4a80a..bac4cf41 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-inline-objects/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden index 74b0cdf4..34fd4c56 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional array of referenced model types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden index c6b9eb58..10fe8402 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-array-of-referenced-types/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/apis/default.rs.golden index 705e664b..ac000692 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional inline objects use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/runtime/error.rs.golden index a76a8aba..d1660d02 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-inline-object/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden index 449505f4..c39253cd 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for optional nested object reference use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden index 57722e49..4eccf364 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-optional-nested-object-reference/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/apis/default.rs.golden index c34c4dfb..e5a4e998 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test case for arrays with primitive items (should not be affected by recursive parsing) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -65,6 +65,15 @@ impl From for TestError { } } +impl From for ApiCallError { + fn from(error: TestError) -> Self { + match error { + TestError::Unexpected(error) => Self::from_api_error("test", error), + TestError::Transport(error) => Self::from_runtime_error("test", error), + } + } +} + impl std::fmt::Display for TestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/runtime/error.rs.golden index f4f0d127..e3dcef48 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-primitive-array/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/apis/default.rs.golden index c9c1c651..24040087 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Self-Referential Object Test — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -69,6 +69,15 @@ impl From for GetFooError { } } +impl From for ApiCallError { + fn from(error: GetFooError) -> Self { + match error { + GetFooError::Unexpected(error) => Self::from_api_error("getFoo", error), + GetFooError::Transport(error) => Self::from_runtime_error("getFoo", error), + } + } +} + impl std::fmt::Display for GetFooError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/runtime/error.rs.golden index 939ea545..88e9812a 100644 --- a/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/recursive-json-self-referential-object/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/request-body-content-types/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/request-body-content-types/src/apis/default.rs.golden index 8eef6fa3..b94766c5 100644 --- a/tests/golden/rust/rust-ureq/request-body-content-types/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/request-body-content-types/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Covers non-JSON request body media types to pin Content-Type emission. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -260,6 +260,15 @@ impl From for PostBinaryError { } } +impl From for ApiCallError { + fn from(error: PostBinaryError) -> Self { + match error { + PostBinaryError::Unexpected(error) => Self::from_api_error("postBinary", error), + PostBinaryError::Transport(error) => Self::from_runtime_error("postBinary", error), + } + } +} + impl std::fmt::Display for PostBinaryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -298,6 +307,15 @@ impl From for PostFormError { } } +impl From for ApiCallError { + fn from(error: PostFormError) -> Self { + match error { + PostFormError::Unexpected(error) => Self::from_api_error("postForm", error), + PostFormError::Transport(error) => Self::from_runtime_error("postForm", error), + } + } +} + impl std::fmt::Display for PostFormError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -336,6 +354,15 @@ impl From for PostJsonError { } } +impl From for ApiCallError { + fn from(error: PostJsonError) -> Self { + match error { + PostJsonError::Unexpected(error) => Self::from_api_error("postJson", error), + PostJsonError::Transport(error) => Self::from_runtime_error("postJson", error), + } + } +} + impl std::fmt::Display for PostJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -374,6 +401,15 @@ impl From for PostJsonOrXmlError { } } +impl From for ApiCallError { + fn from(error: PostJsonOrXmlError) -> Self { + match error { + PostJsonOrXmlError::Unexpected(error) => Self::from_api_error("postJsonOrXml", error), + PostJsonOrXmlError::Transport(error) => Self::from_runtime_error("postJsonOrXml", error), + } + } +} + impl std::fmt::Display for PostJsonOrXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -412,6 +448,15 @@ impl From for PatchMergeJsonError { } } +impl From for ApiCallError { + fn from(error: PatchMergeJsonError) -> Self { + match error { + PatchMergeJsonError::Unexpected(error) => Self::from_api_error("patchMergeJson", error), + PatchMergeJsonError::Transport(error) => Self::from_runtime_error("patchMergeJson", error), + } + } +} + impl std::fmt::Display for PatchMergeJsonError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -450,6 +495,15 @@ impl From for PostTextError { } } +impl From for ApiCallError { + fn from(error: PostTextError) -> Self { + match error { + PostTextError::Unexpected(error) => Self::from_api_error("postText", error), + PostTextError::Transport(error) => Self::from_runtime_error("postText", error), + } + } +} + impl std::fmt::Display for PostTextError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -488,6 +542,15 @@ impl From for PostXmlError { } } +impl From for ApiCallError { + fn from(error: PostXmlError) -> Self { + match error { + PostXmlError::Unexpected(error) => Self::from_api_error("postXml", error), + PostXmlError::Transport(error) => Self::from_runtime_error("postXml", error), + } + } +} + impl std::fmt::Display for PostXmlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -527,6 +590,15 @@ impl From for GetXmlResponseError { } } +impl From for ApiCallError { + fn from(error: GetXmlResponseError) -> Self { + match error { + GetXmlResponseError::Unexpected(error) => Self::from_api_error("getXmlResponse", error), + GetXmlResponseError::Transport(error) => Self::from_runtime_error("getXmlResponse", error), + } + } +} + impl std::fmt::Display for GetXmlResponseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/request-body-content-types/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/request-body-content-types/src/runtime/error.rs.golden index 0d6287da..19e3d623 100644 --- a/tests/golden/rust/rust-ureq/request-body-content-types/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/request-body-content-types/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/apis/widget.rs.golden b/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/apis/widget.rs.golden index d9fe56ff..9a5c7ce6 100644 --- a/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Default And Exact Response API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a> { @@ -70,6 +70,16 @@ impl From for GetWidgetError { } } +impl From for ApiCallError { + fn from(error: GetWidgetError) -> Self { + match error { + GetWidgetError::Default(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Unexpected(error) => Self::from_api_error("getWidget", error), + GetWidgetError::Transport(error) => Self::from_runtime_error("getWidget", error), + } + } +} + impl std::fmt::Display for GetWidgetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/runtime/error.rs.golden index 0184befc..103cdaea 100644 --- a/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-default-and-exact/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/response-body-fallback/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/response-body-fallback/src/apis/default.rs.golden index ca34dc30..027a613c 100644 --- a/tests/golden/rust/rust-ureq/response-body-fallback/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-fallback/src/apis/default.rs.golden @@ -4,7 +4,7 @@ // Response Fallback Test API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -101,6 +101,16 @@ impl From for GetFallbackNoBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackNoBodyError) -> Self { + match error { + GetFallbackNoBodyError::BadRequest(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Unexpected(error) => Self::from_api_error("get_fallback_no_body", error), + GetFallbackNoBodyError::Transport(error) => Self::from_runtime_error("get_fallback_no_body", error), + } + } +} + impl std::fmt::Display for GetFallbackNoBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -142,6 +152,16 @@ impl From for GetFallbackWithBodyError { } } +impl From for ApiCallError { + fn from(error: GetFallbackWithBodyError) -> Self { + match error { + GetFallbackWithBodyError::NotFound(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Unexpected(error) => Self::from_api_error("get_fallback_with_body", error), + GetFallbackWithBodyError::Transport(error) => Self::from_runtime_error("get_fallback_with_body", error), + } + } +} + impl std::fmt::Display for GetFallbackWithBodyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/response-body-fallback/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/response-body-fallback/src/runtime/error.rs.golden index eb7a822d..fd68e337 100644 --- a/tests/golden/rust/rust-ureq/response-body-fallback/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-fallback/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/apis/widget.rs.golden b/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/apis/widget.rs.golden index 5dcc5038..a76343a5 100644 --- a/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/apis/widget.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/apis/widget.rs.golden @@ -4,7 +4,7 @@ // Multi Status API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Widget" tag. pub struct WidgetApi<'a> { @@ -78,6 +78,16 @@ impl From for ListWidgetsError { } } +impl From for ApiCallError { + fn from(error: ListWidgetsError) -> Self { + match error { + ListWidgetsError::NotFound(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Unexpected(error) => Self::from_api_error("listWidgets", error), + ListWidgetsError::Transport(error) => Self::from_runtime_error("listWidgets", error), + } + } +} + impl std::fmt::Display for ListWidgetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/runtime/error.rs.golden index 3dd1e40f..e2910e74 100644 --- a/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-multi-status-responses/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/response-body-no-response-body/src/apis/foo.rs.golden b/tests/golden/rust/rust-ureq/response-body-no-response-body/src/apis/foo.rs.golden index 164e5d54..df8f1bd9 100644 --- a/tests/golden/rust/rust-ureq/response-body-no-response-body/src/apis/foo.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-no-response-body/src/apis/foo.rs.golden @@ -4,7 +4,7 @@ // No Response Body API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Foo" tag. pub struct FooApi<'a> { @@ -77,6 +77,17 @@ impl From for UpdateFooBarError { } } +impl From for ApiCallError { + fn from(error: UpdateFooBarError) -> Self { + match error { + UpdateFooBarError::BadRequest(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::InternalServerError(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Unexpected(error) => Self::from_api_error("updateFooBar", error), + UpdateFooBarError::Transport(error) => Self::from_runtime_error("updateFooBar", error), + } + } +} + impl std::fmt::Display for UpdateFooBarError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/response-body-no-response-body/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/response-body-no-response-body/src/runtime/error.rs.golden index a7e5c71a..3d93df07 100644 --- a/tests/golden/rust/rust-ureq/response-body-no-response-body/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/response-body-no-response-body/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/server-object/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/server-object/src/apis/default.rs.golden index 2abfa4ad..322ea89d 100644 --- a/tests/golden/rust/rust-ureq/server-object/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/server-object/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // This is a test API specification that includes various server configurations use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -108,6 +108,15 @@ impl From for GetAllUsersError { } } +impl From for ApiCallError { + fn from(error: GetAllUsersError) -> Self { + match error { + GetAllUsersError::Unexpected(error) => Self::from_api_error("getAllUsers", error), + GetAllUsersError::Transport(error) => Self::from_runtime_error("getAllUsers", error), + } + } +} + impl std::fmt::Display for GetAllUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -148,6 +157,16 @@ impl From for GetUserByIdError { } } +impl From for ApiCallError { + fn from(error: GetUserByIdError) -> Self { + match error { + GetUserByIdError::NotFound(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Unexpected(error) => Self::from_api_error("getUserById", error), + GetUserByIdError::Transport(error) => Self::from_runtime_error("getUserById", error), + } + } +} + impl std::fmt::Display for GetUserByIdError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/server-object/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/server-object/src/runtime/error.rs.golden index 6f8ed4ad..5d50e45d 100644 --- a/tests/golden/rust/rust-ureq/server-object/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/server-object/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/server-path-prefix/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/server-path-prefix/src/apis/default.rs.golden index 9846191f..dff6a2d3 100644 --- a/tests/golden/rust/rust-ureq/server-path-prefix/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/server-path-prefix/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for server URL path prefix stripping in operation paths. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -115,6 +115,15 @@ impl From for HealthCheckError { } } +impl From for ApiCallError { + fn from(error: HealthCheckError) -> Self { + match error { + HealthCheckError::Unexpected(error) => Self::from_api_error("health_check", error), + HealthCheckError::Transport(error) => Self::from_runtime_error("health_check", error), + } + } +} + impl std::fmt::Display for HealthCheckError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -153,6 +162,15 @@ impl From for ListUsersError { } } +impl From for ApiCallError { + fn from(error: ListUsersError) -> Self { + match error { + ListUsersError::Unexpected(error) => Self::from_api_error("list_users", error), + ListUsersError::Transport(error) => Self::from_runtime_error("list_users", error), + } + } +} + impl std::fmt::Display for ListUsersError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -191,6 +209,15 @@ impl From for GetUserError { } } +impl From for ApiCallError { + fn from(error: GetUserError) -> Self { + match error { + GetUserError::Unexpected(error) => Self::from_api_error("get_user", error), + GetUserError::Transport(error) => Self::from_runtime_error("get_user", error), + } + } +} + impl std::fmt::Display for GetUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/server-path-prefix/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/server-path-prefix/src/runtime/error.rs.golden index eefcb4a6..441239fa 100644 --- a/tests/golden/rust/rust-ureq/server-path-prefix/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/server-path-prefix/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/apis/default.rs.golden index 8ab3a629..3c9de3e1 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for complex union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestComplexUnionError { } } +impl From for ApiCallError { + fn from(error: TestComplexUnionError) -> Self { + match error { + TestComplexUnionError::Unexpected(error) => Self::from_api_error("test_complex_union", error), + TestComplexUnionError::Transport(error) => Self::from_runtime_error("test_complex_union", error), + } + } +} + impl std::fmt::Display for TestComplexUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/runtime/error.rs.golden index cb186be8..bec62b70 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-complex-union/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden index b3bd807f..2e2cdcf0 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/apis/default.rs.golden @@ -14,7 +14,7 @@ // "EventKindUnspecified" instead of generic "Kind". use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -80,6 +80,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden index 7b9b455c..f879ff35 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-inline-discriminator-only/src/runtime/error.rs.golden @@ -112,3 +112,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden index 45653ead..2617ccae 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // See: https://www.openapis.org/understanding-openapi/specification/models/ use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -73,6 +73,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden index bbc5c83c..e7dd6b70 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-internally-tagged/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden index 493d94b3..848bbe68 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/apis/default.rs.golden @@ -7,7 +7,7 @@ // 100-char render width. Ensures PEP 695 parenthesization is correct. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -73,6 +73,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden index 43234de4..2f43830a 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-long-names/src/runtime/error.rs.golden @@ -105,3 +105,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden index 1a9be777..aa7d0c6f 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/apis/default.rs.golden @@ -10,7 +10,7 @@ // is a unit variant (no fields) and others carry data from referenced schemas. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -76,6 +76,15 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::Unexpected(error) => Self::from_api_error("create_resource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("create_resource", error), + } + } +} + impl std::fmt::Display for CreateResourceError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden index 2113dee3..f4c75f54 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-mixed-unit-and-allof/src/runtime/error.rs.golden @@ -108,3 +108,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden index d78132f3..5d463482 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/apis/default.rs.golden @@ -8,7 +8,7 @@ // the same Kind.ts file causing conflicts. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -103,6 +103,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -142,6 +151,15 @@ impl From for CreateVolumeError { } } +impl From for ApiCallError { + fn from(error: CreateVolumeError) -> Self { + match error { + CreateVolumeError::Unexpected(error) => Self::from_api_error("create_volume", error), + CreateVolumeError::Transport(error) => Self::from_runtime_error("create_volume", error), + } + } +} + impl std::fmt::Display for CreateVolumeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden index cf460504..5f88df30 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-multiple/src/runtime/error.rs.golden @@ -106,3 +106,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden index 8b3fb1e1..d6d2638a 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/apis/default.rs.golden @@ -6,7 +6,7 @@ // This is similar to the ContainerImage pattern in the infiron API. use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -72,6 +72,15 @@ impl From for CreateContainerError { } } +impl From for ApiCallError { + fn from(error: CreateContainerError) -> Self { + match error { + CreateContainerError::Unexpected(error) => Self::from_api_error("create_container", error), + CreateContainerError::Transport(error) => Self::from_runtime_error("create_container", error), + } + } +} + impl std::fmt::Display for CreateContainerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden index 632f46a0..6efe3886 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-discriminated-union-with-refs/src/runtime/error.rs.golden @@ -104,3 +104,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/apis/default.rs.golden index 68c72724..caef747f 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestIntersectionError { } } +impl From for ApiCallError { + fn from(error: TestIntersectionError) -> Self { + match error { + TestIntersectionError::Unexpected(error) => Self::from_api_error("test_intersection", error), + TestIntersectionError::Transport(error) => Self::from_runtime_error("test_intersection", error), + } + } +} + impl std::fmt::Display for TestIntersectionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/runtime/error.rs.golden index 74f892cd..04c7e970 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-intersection-allof/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden index 08284dc4..f3dbd10b 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for allOf intersection types with nullable reference properties use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -101,6 +101,15 @@ impl From for CreateTestError { } } +impl From for ApiCallError { + fn from(error: CreateTestError) -> Self { + match error { + CreateTestError::Unexpected(error) => Self::from_api_error("create_test", error), + CreateTestError::Transport(error) => Self::from_runtime_error("create_test", error), + } + } +} + impl std::fmt::Display for CreateTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -140,6 +149,15 @@ impl From for GetTestError { } } +impl From for ApiCallError { + fn from(error: GetTestError) -> Self { + match error { + GetTestError::Unexpected(error) => Self::from_api_error("get_test", error), + GetTestError::Transport(error) => Self::from_runtime_error("get_test", error), + } + } +} + impl std::fmt::Display for GetTestError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden index 4e385cf2..78e8ca6f 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-intersection-with-nullable-reference/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/apis/default.rs.golden index 0e0eb43f..a8f38b98 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that reference other union types use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestNestedUnionError { } } +impl From for ApiCallError { + fn from(error: TestNestedUnionError) -> Self { + match error { + TestNestedUnionError::Unexpected(error) => Self::from_api_error("test_nested_union", error), + TestNestedUnionError::Transport(error) => Self::from_runtime_error("test_nested_union", error), + } + } +} + impl std::fmt::Display for TestNestedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/runtime/error.rs.golden index 972e8b25..98c89bb0 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-nested-union/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/apis/default.rs.golden index 97b2551b..1546e6aa 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for simple type aliases (not unions or intersections) use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestSimpleAliasError { } } +impl From for ApiCallError { + fn from(error: TestSimpleAliasError) -> Self { + match error { + TestSimpleAliasError::Unexpected(error) => Self::from_api_error("test_simple_alias", error), + TestSimpleAliasError::Transport(error) => Self::from_runtime_error("test_simple_alias", error), + } + } +} + impl std::fmt::Display for TestSimpleAliasError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/runtime/error.rs.golden index 37853949..a15992cb 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-simple-type-alias/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/apis/default.rs.golden index 43135706..bf1be2c2 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with both interfaces and primitives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestMixedUnionError { } } +impl From for ApiCallError { + fn from(error: TestMixedUnionError) -> Self { + match error { + TestMixedUnionError::Unexpected(error) => Self::from_api_error("test_mixed_union", error), + TestMixedUnionError::Transport(error) => Self::from_runtime_error("test_mixed_union", error), + } + } +} + impl std::fmt::Display for TestMixedUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/runtime/error.rs.golden index 673a5d37..6763d440 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-mixed/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/apis/default.rs.golden index 11a95e1f..3f384605 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types that include the any type use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestUnionWithAnyError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithAnyError) -> Self { + match error { + TestUnionWithAnyError::Unexpected(error) => Self::from_api_error("test_union_with_any", error), + TestUnionWithAnyError::Transport(error) => Self::from_runtime_error("test_union_with_any", error), + } + } +} + impl std::fmt::Display for TestUnionWithAnyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/runtime/error.rs.golden index 6a19e5cc..d24a3400 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-any/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/apis/default.rs.golden index b5d2f0b6..6616e5e0 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types (oneOf) with inline object schemas that should generate named interfaces use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestUnionWithInlineObjectsError { } } +impl From for ApiCallError { + fn from(error: TestUnionWithInlineObjectsError) -> Self { + match error { + TestUnionWithInlineObjectsError::Unexpected(error) => Self::from_api_error("test_union_with_inline_objects", error), + TestUnionWithInlineObjectsError::Transport(error) => Self::from_runtime_error("test_union_with_inline_objects", error), + } + } +} + impl std::fmt::Display for TestUnionWithInlineObjectsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden index a5c3fea9..d5105951 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-inline-objects/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/apis/default.rs.golden index b24aee40..a54e53c6 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for oneOf/anyOf union types with interface members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestUnionError { } } +impl From for ApiCallError { + fn from(error: TestUnionError) -> Self { + match error { + TestUnionError::Unexpected(error) => Self::from_api_error("test_union", error), + TestUnionError::Transport(error) => Self::from_runtime_error("test_union", error), + } + } +} + impl std::fmt::Display for TestUnionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/runtime/error.rs.golden index 4539330a..ba2a8041 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-interfaces/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/apis/default.rs.golden index a336241f..1ee72d7e 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for union types with primitive members use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for TestUnionPrimitivesError { } } +impl From for ApiCallError { + fn from(error: TestUnionPrimitivesError) -> Self { + match error { + TestUnionPrimitivesError::Unexpected(error) => Self::from_api_error("test_union_primitives", error), + TestUnionPrimitivesError::Transport(error) => Self::from_runtime_error("test_union_primitives", error), + } + } +} + impl std::fmt::Display for TestUnionPrimitivesError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/runtime/error.rs.golden index e6a359b7..ea4a0ae3 100644 --- a/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/type-aliases-union-with-primitives/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/typed-error-responses/src/apis/resources.rs.golden b/tests/golden/rust/rust-ureq/typed-error-responses/src/apis/resources.rs.golden index 82b001ca..b358dbd3 100644 --- a/tests/golden/rust/rust-ureq/typed-error-responses/src/apis/resources.rs.golden +++ b/tests/golden/rust/rust-ureq/typed-error-responses/src/apis/resources.rs.golden @@ -4,7 +4,7 @@ // Typed Error Responses API — 1.0.0 use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "Resources" tag. pub struct ResourcesApi<'a> { @@ -168,6 +168,16 @@ impl From for CheckNoSuccessBodyError { } } +impl From for ApiCallError { + fn from(error: CheckNoSuccessBodyError) -> Self { + match error { + CheckNoSuccessBodyError::ServiceUnavailable(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Unexpected(error) => Self::from_api_error("checkNoSuccessBody", error), + CheckNoSuccessBodyError::Transport(error) => Self::from_runtime_error("checkNoSuccessBody", error), + } + } +} + impl CheckNoSuccessBodyError { fn response_headers(&self) -> Option<&ureq::http::HeaderMap> { match self { @@ -244,6 +254,23 @@ impl From for CreateResourceError { } } +impl From for ApiCallError { + fn from(error: CreateResourceError) -> Self { + match error { + CreateResourceError::BadRequest(error) => Self::from_api_error("createResource", error), + CreateResourceError::Conflict(error) => Self::from_api_error("createResource", error), + CreateResourceError::Validation(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status423(error) => Self::from_api_error("createResource", error), + CreateResourceError::Status424(error) => Self::from_api_error("createResource", error), + CreateResourceError::TooManyRequests(error) => Self::from_api_error("createResource", error), + CreateResourceError::ServerError(error) => Self::from_api_error("createResource", error), + CreateResourceError::Default(error) => Self::from_api_error("createResource", error), + CreateResourceError::Unexpected(error) => Self::from_api_error("createResource", error), + CreateResourceError::Transport(error) => Self::from_runtime_error("createResource", error), + } + } +} + impl CreateResourceError { fn response_headers(&self) -> Option<&ureq::http::HeaderMap> { match self { @@ -334,6 +361,16 @@ impl From for CreateResourceWithWildcardSuccessError { } } +impl From for ApiCallError { + fn from(error: CreateResourceWithWildcardSuccessError) -> Self { + match error { + CreateResourceWithWildcardSuccessError::BadRequest(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Unexpected(error) => Self::from_api_error("createResourceWithWildcardSuccess", error), + CreateResourceWithWildcardSuccessError::Transport(error) => Self::from_runtime_error("createResourceWithWildcardSuccess", error), + } + } +} + impl std::fmt::Display for CreateResourceWithWildcardSuccessError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/typed-error-responses/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/typed-error-responses/src/runtime/error.rs.golden index e7b99a36..4fe52936 100644 --- a/tests/golden/rust/rust-ureq/typed-error-responses/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/typed-error-responses/src/runtime/error.rs.golden @@ -102,3 +102,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/utoipa-mixed/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/utoipa-mixed/src/apis/default.rs.golden index ce848ac3..a326c215 100644 --- a/tests/golden/rust/rust-ureq/utoipa-mixed/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-mixed/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture covering all schema kinds with utoipa enabled use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -161,6 +161,15 @@ impl From for CreateFeedError { } } +impl From for ApiCallError { + fn from(error: CreateFeedError) -> Self { + match error { + CreateFeedError::Unexpected(error) => Self::from_api_error("create_feed", error), + CreateFeedError::Transport(error) => Self::from_runtime_error("create_feed", error), + } + } +} + impl std::fmt::Display for CreateFeedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -200,6 +209,15 @@ impl From for CreatePaymentError { } } +impl From for ApiCallError { + fn from(error: CreatePaymentError) -> Self { + match error { + CreatePaymentError::Unexpected(error) => Self::from_api_error("create_payment", error), + CreatePaymentError::Transport(error) => Self::from_runtime_error("create_payment", error), + } + } +} + impl std::fmt::Display for CreatePaymentError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -239,6 +257,15 @@ impl From for ListPetsError { } } +impl From for ApiCallError { + fn from(error: ListPetsError) -> Self { + match error { + ListPetsError::Unexpected(error) => Self::from_api_error("list_pets", error), + ListPetsError::Transport(error) => Self::from_runtime_error("list_pets", error), + } + } +} + impl std::fmt::Display for ListPetsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -278,6 +305,15 @@ impl From for CreatePetError { } } +impl From for ApiCallError { + fn from(error: CreatePetError) -> Self { + match error { + CreatePetError::Unexpected(error) => Self::from_api_error("create_pet", error), + CreatePetError::Transport(error) => Self::from_runtime_error("create_pet", error), + } + } +} + impl std::fmt::Display for CreatePetError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/utoipa-mixed/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/utoipa-mixed/src/runtime/error.rs.golden index 555c5834..028a4a75 100644 --- a/tests/golden/rust/rust-ureq/utoipa-mixed/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-mixed/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/apis/default.rs.golden index 43660a68..c0161df4 100644 --- a/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test fixture for untagged union manual utoipa impl generation use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for CreateEventError { } } +impl From for ApiCallError { + fn from(error: CreateEventError) -> Self { + match error { + CreateEventError::Unexpected(error) => Self::from_api_error("create_event", error), + CreateEventError::Transport(error) => Self::from_runtime_error("create_event", error), + } + } +} + impl std::fmt::Display for CreateEventError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/runtime/error.rs.golden index 216ed049..e7186ea7 100644 --- a/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-untagged-union/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/apis/default.rs.golden b/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/apis/default.rs.golden index 3e2f701c..8bab43cd 100644 --- a/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/apis/default.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/apis/default.rs.golden @@ -5,7 +5,7 @@ // Test that utoipa::ToSchema is additive alongside user extra_derives use crate::runtime::client::Client; -use crate::runtime::error::{ApiError, Error}; +use crate::runtime::error::{ApiCallError, ApiError, Error}; /// API operations under the "default" tag. pub struct DefaultApi<'a> { @@ -71,6 +71,15 @@ impl From for CreateItemError { } } +impl From for ApiCallError { + fn from(error: CreateItemError) -> Self { + match error { + CreateItemError::Unexpected(error) => Self::from_api_error("create_item", error), + CreateItemError::Transport(error) => Self::from_runtime_error("create_item", error), + } + } +} + impl std::fmt::Display for CreateItemError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/runtime/error.rs.golden b/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/runtime/error.rs.golden index 689c6890..13222063 100644 --- a/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/runtime/error.rs.golden +++ b/tests/golden/rust/rust-ureq/utoipa-with-extra-derives/src/runtime/error.rs.golden @@ -103,3 +103,107 @@ impl ApiError { self.body.as_ref() } } + +#[derive(Debug)] +struct ApiCallHttpError { + status_code: u16, + headers: ureq::http::HeaderMap, + raw_body: Vec, + body_error: Option, +} + +impl ApiCallHttpError { + fn from_api_error(error: ApiError) -> Self { + Self { + status_code: error.status_code, + headers: error.headers, + raw_body: error.raw_body, + body_error: error.body.err(), + } + } +} + +#[derive(Debug)] +enum ApiCallErrorKind { + Http(ApiCallHttpError), + Runtime(Error), +} + +/// Type-erased error from any generated API operation. +#[derive(Debug)] +pub struct ApiCallError { + operation_id: &'static str, + kind: ApiCallErrorKind, +} + +impl ApiCallError { + pub(crate) fn from_api_error(operation_id: &'static str, error: ApiError) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Http(ApiCallHttpError::from_api_error(error)), + } + } + + pub(crate) fn from_runtime_error(operation_id: &'static str, error: Error) -> Self { + Self { + operation_id, + kind: ApiCallErrorKind::Runtime(error), + } + } + + /// Operation identifier, generated from the HTTP method and path when OpenAPI omits one. + pub fn operation_id(&self) -> &'static str { + self.operation_id + } + + /// HTTP response status, if the operation reached the server and received an error response. + pub fn status_code(&self) -> Option { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(error.status_code), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Native HTTP response headers, if an error response was received. + pub fn headers(&self) -> Option<&ureq::http::HeaderMap> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.headers), + ApiCallErrorKind::Runtime(_) => None, + } + } + + /// Raw HTTP response body, if an error response was received. + pub fn raw_body(&self) -> Option<&[u8]> { + match &self.kind { + ApiCallErrorKind::Http(error) => Some(&error.raw_body), + ApiCallErrorKind::Runtime(_) => None, + } + } +} + +impl std::fmt::Display for ApiCallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + ApiCallErrorKind::Http(error) => { + write!(f, "operation {} failed with HTTP status {}", self.operation_id, error.status_code) + } + ApiCallErrorKind::Runtime(error) => { + write!(f, "operation {} failed: {error}", self.operation_id) + } + } + } +} + +impl std::error::Error for ApiCallError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + if let ApiCallErrorKind::Http(error) = &self.kind { + if let Some(error) = &error.body_error { + return Some(error); + } + } + if let ApiCallErrorKind::Runtime(error) = &self.kind { + return Some(error); + } + None + } +} diff --git a/tests/typed_http_error_runtime.rs b/tests/typed_http_error_runtime.rs index c4c3fb86..ba49b0f8 100644 --- a/tests/typed_http_error_runtime.rs +++ b/tests/typed_http_error_runtime.rs @@ -96,6 +96,8 @@ fn add_reqwest_runtime_test(root: &Path) { [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +thiserror = "2" +snafu = "0.9.1" "#, ); fs::write(cargo_toml, manifest).expect("generated Cargo.toml should be updated"); @@ -112,7 +114,62 @@ use typed_error_responses_api::apis::{ }; use typed_error_responses_api::models::CreateResourceRequest; use typed_error_responses_api::runtime::client::Client; -use typed_error_responses_api::runtime::error::Error; +use typed_error_responses_api::runtime::error::{ApiCallError, Error}; + +#[allow(dead_code)] +async fn propagate_uniformly( + api: &ResourcesApi<'_>, + request: &CreateResourceRequest, +) -> Result<(), ApiCallError> { + api.create_resource(request).await?; + api.check_no_success_body().await?; + Ok(()) +} + +#[derive(Debug, thiserror::Error)] +#[error("SDK request failed: {source}")] +struct ThisErrorAppError { + #[source] + source: ApiCallError, +} + +impl From for ThisErrorAppError +where + E: Into, +{ + fn from(error: E) -> Self { + Self { + source: error.into(), + } + } +} + +#[allow(dead_code)] +async fn propagate_with_thiserror( + api: &ResourcesApi<'_>, + request: &CreateResourceRequest, +) -> Result<(), ThisErrorAppError> { + api.create_resource(request).await?; + api.check_no_success_body().await?; + Ok(()) +} + +#[derive(Debug, snafu::Snafu)] +#[snafu(context(false))] +struct SnafuAppError { + #[snafu(source(from(generic)))] + source: ApiCallError, +} + +#[allow(dead_code)] +async fn propagate_with_snafu( + api: &ResourcesApi<'_>, + request: &CreateResourceRequest, +) -> Result<(), SnafuAppError> { + api.create_resource(request).await?; + api.check_no_success_body().await?; + Ok(()) +} #[tokio::test] async fn http_503_returns_operation_error_with_typed_body() { @@ -178,11 +235,68 @@ async fn invalid_error_body_keeps_http_error_and_raw_body() { assert_eq!(api_error.status_code(), 503); assert_eq!(api_error.raw_body(), body.as_bytes()); assert!(matches!(api_error.body(), Err(Error::Deserialize(_)))); + + let error: ApiCallError = CreateResourceError::ServerError(api_error).into(); + assert_eq!(error.operation_id(), "createResource"); + assert_eq!(error.status_code(), Some(503)); + assert_eq!(error.raw_body(), Some(body.as_bytes())); + assert!(matches!( + std::error::Error::source(&error).and_then(|source| source.downcast_ref::()), + Some(Error::Deserialize(_)) + )); } other => panic!("expected ServerError variant, got {other:?}"), } } +#[tokio::test] +async fn operation_error_converts_to_uniform_api_call_error() { + let body = r#"{"message":"temporarily unavailable","retryable":true}"#; + let base_url = spawn_one_response_server("503 Service Unavailable", body); + let client = Client::new(&base_url); + let api = ResourcesApi::new(&client); + let request = CreateResourceRequest { + name: "resource".to_string(), + }; + + let error: ApiCallError = api + .create_resource(&request) + .await + .expect_err("HTTP 503 must be returned as an operation error") + .into(); + + assert_eq!(error.operation_id(), "createResource"); + assert_eq!(error.status_code(), Some(503)); + assert_eq!(error.raw_body(), Some(body.as_bytes())); + assert_eq!( + error + .headers() + .and_then(|headers| headers.get("retry-after")) + .and_then(|value| value.to_str().ok()), + Some("120") + ); + assert_eq!( + error.to_string(), + "operation createResource failed with HTTP status 503" + ); + assert!(std::error::Error::source(&error).is_none()); +} + +#[test] +fn transport_error_converts_to_uniform_api_call_error() { + let error: ApiCallError = + CreateResourceError::Transport(Error::Unsupported("test transport failure")).into(); + + assert_eq!(error.operation_id(), "createResource"); + assert_eq!(error.status_code(), None); + assert!(error.headers().is_none()); + assert!(error.raw_body().is_none()); + assert!(matches!( + std::error::Error::source(&error).and_then(|source| source.downcast_ref::()), + Some(Error::Unsupported("test transport failure")) + )); +} + #[tokio::test] async fn exact_success_status_wins_over_2xx_wildcard() { let body = r#"{"id":"exact-created"}"#; @@ -275,6 +389,17 @@ fn add_ureq_runtime_test(root: &Path) { use typed_error_responses_api::apis::{CreateResourceError, ResourcesApi}; use typed_error_responses_api::models::CreateResourceRequest; use typed_error_responses_api::runtime::client::Client; +use typed_error_responses_api::runtime::error::ApiCallError; + +#[allow(dead_code)] +fn propagate_uniformly( + api: &ResourcesApi<'_>, + request: &CreateResourceRequest, +) -> Result<(), ApiCallError> { + api.create_resource(request)?; + api.check_no_success_body()?; + Ok(()) +} #[test] fn http_503_returns_operation_error_with_typed_body() { @@ -319,6 +444,33 @@ fn http_503_returns_operation_error_with_typed_body() { } } +#[test] +fn operation_error_converts_to_uniform_api_call_error() { + let body = r#"{"message":"temporarily unavailable","retryable":true}"#; + let base_url = spawn_one_response_server("503 Service Unavailable", body); + let client = Client::new(&base_url); + let api = ResourcesApi::new(&client); + let request = CreateResourceRequest { + name: "resource".to_string(), + }; + + let error: ApiCallError = api + .create_resource(&request) + .expect_err("HTTP 503 must be returned as an operation error") + .into(); + + assert_eq!(error.operation_id(), "createResource"); + assert_eq!(error.status_code(), Some(503)); + assert_eq!(error.raw_body(), Some(body.as_bytes())); + assert_eq!( + error + .headers() + .and_then(|headers| headers.get("retry-after")) + .and_then(|value| value.to_str().ok()), + Some("120") + ); +} + #[test] fn success_without_documented_body_does_not_drain_body() { let base_url = spawn_incomplete_success_body_server();