From 3ec813f602b59bcb7a5ba3d3392b1d7ea36977f6 Mon Sep 17 00:00:00 2001 From: Adam Basfop Cavendish Date: Fri, 31 Jul 2026 19:57:29 +0800 Subject: [PATCH] feat(rust): add uniform API call errors - Preserve operation-specific typed errors while converting each into ApiCallError - Retain operation IDs, status, native headers, raw bodies, and error sources - Document and compile-test direct ?, thiserror, and SNAFU integration - Emit uniform error machinery only for operation-bearing SDKs - Regenerate and compile-check reqwest, ureq, and aioduct clients --- CHANGELOG.md | 3 + docs/src/rust_config.md | 75 +++++ src/generators/rust/aioduct/codegen.rs | 1 + src/generators/rust/aioduct/runtime.rs | 13 +- src/generators/rust/common/emit_api.rs | 81 ++++++ src/generators/rust/common/mod.rs | 1 + src/generators/rust/common/runtime.rs | 262 ++++++++++++++++++ src/generators/rust/reqwest/codegen.rs | 6 +- src/generators/rust/reqwest/runtime.rs | 18 +- src/generators/rust/ureq/codegen.rs | 6 +- src/generators/rust/ureq/runtime.rs | 18 +- .../src/apis/additional_properties.rs.golden | 32 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_resource.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/items.rs.golden | 12 +- .../src/apis/users.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../enum-repr/src/apis/enum_repr.rs.golden | 52 +++- .../enum-repr/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/media.rs.golden | 56 +++- .../src/runtime/error.rs.golden | 104 +++++++ .../minimal/src/apis/default.rs.golden | 11 +- .../minimal/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_api.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 38 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../petstore/src/apis/pet.rs.golden | 85 +++++- .../petstore/src/apis/store.rs.golden | 43 ++- .../petstore/src/apis/user.rs.golden | 72 ++++- .../petstore/src/runtime/error.rs.golden | 104 +++++++ .../query-param-enum/src/apis/items.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 74 ++++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/foo.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../server-object/src/apis/default.rs.golden | 21 +- .../server-object/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 29 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/resources.rs.golden | 39 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../utoipa-mixed/src/apis/default.rs.golden | 38 ++- .../utoipa-mixed/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/additional_properties.rs.golden | 32 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_resource.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/items.rs.golden | 12 +- .../src/apis/users.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../enum-repr/src/apis/enum_repr.rs.golden | 52 +++- .../enum-repr/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/media.rs.golden | 56 +++- .../src/runtime/error.rs.golden | 104 +++++++ .../minimal/src/apis/default.rs.golden | 11 +- .../minimal/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_api.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 38 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../petstore/src/apis/pet.rs.golden | 85 +++++- .../petstore/src/apis/store.rs.golden | 43 ++- .../petstore/src/apis/user.rs.golden | 72 ++++- .../petstore/src/runtime/error.rs.golden | 104 +++++++ .../query-param-enum/src/apis/items.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 74 ++++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/foo.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../server-object/src/apis/default.rs.golden | 21 +- .../server-object/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 29 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/resources.rs.golden | 39 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../utoipa-mixed/src/apis/default.rs.golden | 38 ++- .../utoipa-mixed/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/additional_properties.rs.golden | 32 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_resource.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/items.rs.golden | 12 +- .../src/apis/users.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../enum-repr/src/apis/enum_repr.rs.golden | 52 +++- .../enum-repr/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/media.rs.golden | 56 +++- .../src/runtime/error.rs.golden | 104 +++++++ .../minimal/src/apis/default.rs.golden | 11 +- .../minimal/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/multipart.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/transfer.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/test_api.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 38 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../rust-ureq/petstore/src/apis/pet.rs.golden | 85 +++++- .../petstore/src/apis/store.rs.golden | 43 ++- .../petstore/src/apis/user.rs.golden | 72 ++++- .../petstore/src/runtime/error.rs.golden | 104 +++++++ .../query-param-enum/src/apis/items.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 74 ++++- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 22 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/widget.rs.golden | 12 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/foo.rs.golden | 13 +- .../src/runtime/error.rs.golden | 104 +++++++ .../server-object/src/apis/default.rs.golden | 21 +- .../server-object/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 29 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 20 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/resources.rs.golden | 39 ++- .../src/runtime/error.rs.golden | 104 +++++++ .../utoipa-mixed/src/apis/default.rs.golden | 38 ++- .../utoipa-mixed/src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ .../src/apis/default.rs.golden | 11 +- .../src/runtime/error.rs.golden | 104 +++++++ tests/typed_http_error_runtime.rs | 154 +++++++++- 393 files changed, 23550 insertions(+), 203 deletions(-) create mode 100644 src/generators/rust/common/runtime.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d3795df9f..cd05b2cf8 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 97d151333..32d3b5812 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 d8cbd9573..ff6303cb0 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 abcd35e92..230ccc871 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 252948b46..3a81d202a 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 4f1e9b71f..4665fc76b 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 000000000..49b53fd7f --- /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 7872c4f0b..8eb38cf34 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 beac9ab26..923357b62 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 d9ddd6ef6..ab015129e 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 89e8d02e9..0349880a7 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 dbbf596f6..a458832d8 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 405f89f72..2efbdf513 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 317f71f5d..127e4705b 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 8029c74b0..9db23476b 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 e9380f4d8..2a63b3e2c 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 e480d151f..24133ff06 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 7b0cee1a5..cbc1a4213 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 1592ccbc6..d34013bcf 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 2dfefe06b..01448d231 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 62ff9772a..326d1fc10 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 2d83f6e2c..9d75448e7 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 ef5bb0dfe..71e8e035d 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 88cf3cbaf..c3f68c65b 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 c296c18f1..eaa4a84f7 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 43f5c77d5..9e412f088 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 59789e2dd..409098030 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 059f45149..28fea5aa0 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 9ee252c22..940ccd354 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 9953be99f..052a83b9e 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 b8cf6a136..8b907eeb6 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 10976e38a..da0d971c0 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 284fe7542..3304122ac 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 133799b6d..b5b14ff3c 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 ede4860f4..c41c7bca5 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 e00e59c7b..6240d343e 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 dd2e2ebe6..7dd7eaef9 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 cfb5f9199..f582a28ce 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 5d535cd7b..e18d774cb 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 49a50093e..e4a7b4a36 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 539242701..d11e2a6ba 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 b46e3bd27..4d4616f47 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 e47e9d3ba..433160df4 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 ecf911046..79bc5e467 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 c5ca9035d..8e7543b68 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 0acc22f75..61ec0b694 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 c55602fac..f46ef3fe3 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 5cc4dc9f5..d7de47a17 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 abcfee3c7..6323f4742 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 c8877d750..243cef687 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 520b9a3e5..19f522fa3 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 d75b38bec..ef9d914d9 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 e202c690c..61a61f4c6 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 711cca33a..3c6effaec 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 64a734b6f..83d54b316 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 db3a36f7f..515477ab4 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 817b04744..7a21951cd 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 58b7e0505..10b22d04c 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 5315d457a..0d101f68d 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 341961ef2..7ceb584ee 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 0e1873e69..d50c14a93 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 b33481ce5..b8220126b 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 f98af1a39..8c2ca082d 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 639adef44..5221954ad 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 8eb0f0004..38fb9557f 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 97b4ed2d5..337006c10 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 66ec63744..77ad34117 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 ad008e801..d7995c738 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 651a74b94..9c408038f 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 1ef132216..d7a8b747f 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 de53794a1..fac6529a7 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 5f619d57d..b5f8dd3ed 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 6e5d06f14..ef9ab6448 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 aff8e1fc0..e2b493cb2 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 5c129c28e..5b7853063 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 b5e13631a..8d677597f 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 3abad03ce..b63728b2b 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 228b1b198..1d857ca34 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 46a6aa08e..9bd8a84d6 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 f06ec957d..c9a66bc73 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 5f506667d..1669fb6a6 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 c38b52b1e..fc4135a50 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 9551bf8c9..12347d6f4 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 a83f70d7e..4c06534c3 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 925a2019d..72f4cf9b0 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 09294dc44..2432ce0cd 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 c31b14b40..9e998f960 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 c7d3e4532..e3b6ab362 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 fd6d08d5c..ff700a38e 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 1ae8ffb27..10606ed79 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 fcb35c8b3..63cb06508 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 ead62ceb2..d4a035ce0 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 7a5cba4ab..9822d287f 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 c57d37043..3984752cd 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 2f0b15ffc..31d845563 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 6366ecd7a..c40c1926a 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 0a2843aa2..355ed1402 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 5c4ee1ec0..c5d772848 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 97665b124..af16f2f77 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 8c9f6914b..e9d91613d 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 ece2fb844..1f3190044 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 e00a9ac2c..edba25ba4 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 aaf12b818..e2eeecd9e 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 2c3b7d238..27246ea25 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 fa664d890..1ecbb2ead 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 432079876..21bdd8eda 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 29b03d85d..9aba2610d 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 da1befe2a..7fa314936 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 f1047260c..ebe39201b 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 885fb9337..e960dbfe8 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 874cccb57..1d1825780 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 25cbb488b..224e1a367 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 90ec7185f..d980737f4 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 8473435c1..e548ae5cc 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 01d79f491..df454180c 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 31d6471b7..463cb1660 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 7d0d88e76..227b6d537 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 9cc57dbb7..23e49df7d 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 f780b102c..1a686950e 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 3f27e56a7..f2cc74d60 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 5d3ad8e37..809891c41 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 b8654d100..8c1b2f314 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 0f7407bc7..a5c7c38a0 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 e5552e0eb..0cc9fe1a7 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 508fb3ed3..eef790c12 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 79d557289..69e8f6fa4 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 17ad20dc9..e8e369247 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 c3124fa10..029e44276 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 62a851201..57fbd6bea 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 8d652e889..5de3faea0 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 5a1f0f843..cae1ee23a 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 22410a51a..0ec53e7fe 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 e50bcfff0..979741a7f 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 ed8bf9d9a..61b4d3223 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 31e929d91..3235a9132 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 aba9be21c..3d43a9c0d 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 780c6e0ef..6b4ce1799 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 72caeb495..cc375efb9 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 508eb395f..4faa88268 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 5b32d610e..fd33cedc2 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 8455dd1a0..e6d283bac 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 5bc381985..00dda3737 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 f8f603003..6701291de 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 275c46d73..6c0874596 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 c0f667f31..2b0295928 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 892352b2f..64bd2e286 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 cfbc04ab3..eead23132 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 49951635f..3146153bc 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 ef8302128..3fcf4ce72 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 aa7bb06cd..50a30f52a 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 49438bcba..810cf9e16 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 7e7919943..09053aa17 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 65540a9d1..df2d6eefc 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 28fbf462e..ccc1430c7 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 a4f3d885a..390f71132 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 db2e4c0e7..db52f6f81 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 e2c53c067..b3779e86b 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 932915ecc..6f043ab00 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 0a9757dd1..ddcac1bb2 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 11c6e3939..f9037d003 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 4be568bc2..6a5b31ad6 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 4193a5eb2..ced429fa1 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 c3f32ef4d..9132a7e88 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 fad7945d0..20e169e63 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 dee1197fc..f47b4e3a4 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 be63675c1..07ae73841 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 fa83ed869..dd6731049 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 e19f96631..d91a94ba3 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 9fc0f9cf8..8b3023619 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 8b6977f73..2929b6172 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 fb8414114..17a4f0c8f 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 3a34c5b4d..af90d933a 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 5e1968ad1..50de5df12 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 5b26e1c53..5c2b3a530 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 c2ad4e4d0..a6e693e20 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 175559ae1..552ba4215 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 55685ecc7..a45ac4a02 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 9324bcbfe..efa1a250e 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 ed938b333..05f6b518e 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 35bcd8db8..0a30169bf 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 17d8d5d94..e57e34e1a 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 192c6a906..5853b3487 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 5a9d0dcd0..79b0dae0c 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 dedb8fde8..c8bf62738 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 7dae00c27..f60b49c51 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 4d4d7e5d2..25152e1a0 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 ca84c5117..f8b655237 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 103fa4125..551c97b77 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 fb217c42a..d581a45aa 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 508d7b23a..5b86b61c4 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 82d50510d..9bc97b4ae 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 5a314ca7f..1a0bc0813 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 8b2f4a1ac..034a34b24 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 b39f769e1..177f5d307 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 0d7749e7b..31ae40286 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 81690d184..2ac44d7a6 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 97df5c69a..4072f32fa 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 3e7c9f4ca..100eb0196 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 fa13bfbe8..d95141c47 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 26bb2a9d2..fcb6f33bb 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 e9d92cba6..96ecd07c0 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 722b28d86..040f81641 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 18f0bcc99..4fc5c7d15 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 a7d3ca617..c9d3cf3da 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 3463a21b8..110c58ecb 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 2dc4f7f54..ff3047fb7 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 3baf8cfab..71e97afa4 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 46bba8960..3304c68bb 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 1783dc7fe..267893b46 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 7e951d6af..3ea7f289b 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 2200204a1..d28199a15 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 3b1913fc6..7691404a2 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 c09e84e82..41474c3bf 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 97e3b6f75..858959bdf 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 eab19584a..d9edaa35f 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 dac294c64..35c1ed901 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 266fa3eb3..38d5d509d 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 a5aa4ae45..7f5a15495 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 e9175dc12..ae1afe521 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 71ff0980f..c6b6d013f 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 af8a0700b..093578f2e 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 01cc6eeb6..3fbb4d781 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 22340a33c..3a61c25c9 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 44825fc6e..2e86d3b87 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 2d40d97e8..7b3e8d93d 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 eb6ac5b31..e971004c5 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 43c41bf01..88de117ac 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 2e4b182eb..b1190b5ac 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 2d539d617..21a9c332f 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 bd723a7b3..ff507d9d5 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 c826a0ef1..2e5cc67e2 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 4ebac499f..86f91ee05 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 7facd8052..8a3ede0eb 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 c92b0649a..1cd5f3507 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 238154f08..726e5b04c 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 8c2060cd9..5d9d01c97 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 9826ad160..84a4f596e 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 dc3615382..4d081c566 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 6fa495072..089168197 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 636149d66..3e84fbbe3 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 ea377f1f7..45f8a0877 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 856eba42c..b18ce3fef 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 91e0199bd..e66f0af4d 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 77872b24a..d5845807e 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 b4723f0ce..6eee8ad3a 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 0806b5ada..ba1bad295 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 5715a45c7..f516f8292 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 a64f746a0..74a59507d 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 63da1dcf4..e288378ee 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 d762c8755..ec4688064 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 5712e27d2..45c71e1ce 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 5e7375f99..f43e5e6b8 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 40e34525e..32ad3d146 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 1de19a441..446e57ff5 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 33a2ccc2a..119025265 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 14630496d..9cca7e855 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 0994ea52c..f0fb1614a 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 4d1b5443f..166095861 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 e05104ade..bcd50b314 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 1fb1c2601..7e8e56919 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 1e86bf750..1120d985f 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 7081f6934..1374b922a 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 620574da5..02afd6848 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 b3a1661f4..b41a23bda 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 f6421cac4..8f5fc1e53 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 91f258cf2..46a9e4436 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 814da6999..47f6fbc33 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 fbd21642c..7a1b9c56d 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 871ca7471..3935b1f00 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 4ab1c2db4..e46ea4d5a 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 2ee07b2b3..9a4f74b0a 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 97a51c2c7..467d7ba82 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 7110d274c..54495d831 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 1ee7de00b..48ea7c9d8 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 3321a906c..e5e47bedf 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 3f176acb1..a902529b7 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 78c12bce1..b11193ed6 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 41e1f254c..b46f815d5 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 f6c824679..509ecbef3 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 2398107ed..168d48295 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 5d479be46..ec47af058 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 d6dfb82f1..79141951f 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 13c489527..db03acadd 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 51604d025..1137d9df6 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 fcc36c1de..ac97f3047 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 3ad700582..a5dac92c7 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 b507b500c..c462500a2 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 05decd588..66ebce8bb 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 ce2700eef..cd833394e 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 2abd3d84e..de770deb1 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 128b1360c..b92da6501 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 5ec86d5a4..e3fd84bb3 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 6cda3c91f..62629da93 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 b2ed35c73..c48b093d0 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 4f6afa1d1..7491ddbca 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 49364d3cc..047fc825e 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 90294a2c7..944fcc29e 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 079902254..2cf88c0ed 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 5b0d18235..b50ed0253 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 7282ebc4a..2c31a0379 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 48fb0875f..553f0b632 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 f971f9dbd..e49089239 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 97ad0a581..94a0c20cf 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 cfbd319ca..a18cd3993 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 815d4740b..a5ed8febc 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 f2e947020..8e8b5f054 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 f02eb3b1f..9d0a6dd16 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 a9a4c4bb0..f15cf5ec0 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 587833e41..694591744 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 58bebf58b..cc890db04 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 696fa3bde..c74d14dd7 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 7a2d9e2ab..db6e0ad08 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 61789ffa6..169b7df66 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 175d701e0..50bb38862 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 1fd50bc01..844d7320d 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 477801dda..88ed7382a 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 4eb459dde..1cc265c13 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 ddc706e92..d329d5250 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 c1d3defda..b887fff29 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 2657a74a5..84c999140 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 6bb6f3ecf..8f50c3f1a 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 4752a4c3b..a5236a036 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 0815fd070..caf7f9685 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 3e39c06e1..6402ebcec 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 a62568bd3..9738dd508 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 aea9f8557..995d5c984 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 6ae38d6f7..831922ad6 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 47b4a80a0..bac4cf412 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 74b0cdf4b..34fd4c565 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 c6b9eb58c..10fe8402f 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 705e664b1..ac0006927 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 a76a8aba6..d1660d02b 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 449505f4b..c39253cd0 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 57722e498..4eccf3641 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 c34c4dfb5..e5a4e9980 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 f4f0d1272..e3dcef482 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 c9c1c6513..240400876 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 939ea545e..88e9812a2 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 8eef6fa3d..b94766c54 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 0d6287daf..19e3d623e 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 d9fe56ffa..9a5c7ce6c 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 0184befca..103cdaeae 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 ca34dc309..027a613cd 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 eb7a822d7..fd68e3374 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 5dcc50383..a76343a5a 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 3dd1e40ff..e2910e748 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 164e5d544..df8f1bd91 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 a7e5c71a9..3d93df075 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 2abfa4ada..322ea89d0 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 6f8ed4ad8..5d50e45dc 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 9846191fb..dff6a2d31 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 eefcb4a67..441239fa0 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 8ab3a629e..3c9de3e1c 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 cb186be8e..bec62b701 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 b3bd807f3..2e2cdcf0e 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 7b9b455cc..f879ff358 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 45653ead0..2617ccaee 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 bbc5c83c3..e7dd6b70b 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 493d94b35..848bbe68a 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 43234de42..2f43830a4 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 1a9be7775..aa7d0c6f7 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 2113dee39..f4c75f54d 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 d78132f35..5d4634827 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 cf4605048..5f88df304 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 8b3fb1e1d..d6d2638a6 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 632f46a05..6efe3886f 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 68c727244..caef747f5 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 74f892cd4..04c7e9707 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 08284dc4e..f3dbd10b9 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 4e385cf2a..78e8ca6f7 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 0e0eb43f7..a8f38b987 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 972e8b255..98c89bb0d 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 97b2551b2..1546e6aa3 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 37853949c..a15992cbd 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 431357067..bf1be2c2d 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 673a5d373..6763d4403 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 11a95e1f2..3f3846056 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 6a19e5ccc..d24a34007 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 b5d2f0b68..6616e5e06 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 a5c3fea9b..d51059515 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 b24aee403..a54e53c6f 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 4539330a8..ba2a8041f 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 a336241f6..1ee72d7ef 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 e6a359b78..ea4a0ae36 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 82b001cae..b358dbd3d 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 e7b99a36f..4fe529361 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 ce848ac32..a326c2158 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 555c5834f..028a4a75b 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 43660a685..c0161df4c 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 216ed0492..e7186ea79 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 3e2f701c2..8bab43cd6 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 689c6890b..132220633 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 c4c3fb863..ba49b0f8b 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();