diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b6fd4..054b05f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,13 @@ All notable changes to this project will be documented in this file. - `masterror::Error` now uses the in-tree derive, removing the dependency on `thiserror` while keeping the same runtime behaviour and diagnostics. +## [0.5.5] - 2025-09-27 + +### Fixed +- Derive formatter generation now matches on every `TemplateFormatter` + variant and calls the corresponding `::core::fmt` trait (including the + default `Display` path), mirroring `thiserror`'s placeholder handling. + ## [0.5.4] - 2025-09-26 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index fef15de..cfef7f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,7 +1527,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.5.4" +version = "0.5.5" dependencies = [ "actix-web", "axum", @@ -1557,7 +1557,7 @@ dependencies = [ [[package]] name = "masterror-derive" -version = "0.1.2" +version = "0.1.3" dependencies = [ "masterror-template", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 747bcee..927a370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.5.4" +version = "0.5.5" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" @@ -49,7 +49,7 @@ turnkey = [] openapi = ["dep:utoipa"] [workspace.dependencies] -masterror-derive = { version = "0.1.2", path = "masterror-derive" } +masterror-derive = { version = "0.1.3", path = "masterror-derive" } masterror-template = { version = "0.1.2", path = "masterror-template" } [dependencies] diff --git a/README.md b/README.md index eb38a92..3afdd70 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Stable categories, conservative HTTP mapping, no `unsafe`. ~~~toml [dependencies] -masterror = { version = "0.5.4", default-features = false } +masterror = { version = "0.5.5", default-features = false } # or with features: -# masterror = { version = "0.5.4", features = [ +# masterror = { version = "0.5.5", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -66,10 +66,10 @@ masterror = { version = "0.5.4", default-features = false } ~~~toml [dependencies] # lean core -masterror = { version = "0.5.4", default-features = false } +masterror = { version = "0.5.5", default-features = false } # with Axum/Actix + JSON + integrations -# masterror = { version = "0.5.4", features = [ +# masterror = { version = "0.5.5", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -262,13 +262,13 @@ assert_eq!(resp.status, 401); Minimal core: ~~~toml -masterror = { version = "0.5.4", default-features = false } +masterror = { version = "0.5.5", default-features = false } ~~~ API (Axum + JSON + deps): ~~~toml -masterror = { version = "0.5.4", features = [ +masterror = { version = "0.5.5", features = [ "axum", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } @@ -277,7 +277,7 @@ masterror = { version = "0.5.4", features = [ API (Actix + JSON + deps): ~~~toml -masterror = { version = "0.5.4", features = [ +masterror = { version = "0.5.5", features = [ "actix", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } diff --git a/masterror-derive/Cargo.toml b/masterror-derive/Cargo.toml index 26e6b09..f8d2ecb 100644 --- a/masterror-derive/Cargo.toml +++ b/masterror-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "masterror-derive" rust-version = "1.90" -version = "0.1.2" +version = "0.1.3" edition = "2024" license = "MIT OR Apache-2.0" repository = "https://github.com/RAprogramm/masterror" diff --git a/masterror-derive/src/display.rs b/masterror-derive/src/display.rs index b1627c1..21564f6 100644 --- a/masterror-derive/src/display.rs +++ b/masterror-derive/src/display.rs @@ -338,87 +338,71 @@ fn format_placeholder( } = resolved; match formatter { - TemplateFormatter::Display => quote! { - core::fmt::Display::fmt(#expr, f)?; - }, + TemplateFormatter::Display => format_with_trait(expr, "Display"), TemplateFormatter::Debug { - alternate: false - } => quote! { - core::fmt::Debug::fmt(#expr, f)?; - }, - TemplateFormatter::Debug { - alternate: true - } => quote! { - write!(f, "{:#?}", #expr)?; - }, + alternate + } => format_with_optional_alternate(expr, "Debug", '?', alternate), TemplateFormatter::LowerHex { alternate - } => { - if alternate { - quote! { write!(f, "{:#x}", #expr)?; } - } else { - quote! { core::fmt::LowerHex::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "LowerHex", 'x', alternate), TemplateFormatter::UpperHex { alternate - } => { - if alternate { - quote! { write!(f, "{:#X}", #expr)?; } - } else { - quote! { core::fmt::UpperHex::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "UpperHex", 'X', alternate), TemplateFormatter::Pointer { alternate - } => { - if alternate { - quote! { write!(f, "{:#p}", #expr)?; } - } else if pointer_value { - quote! {{ - let value = #expr; - core::fmt::Pointer::fmt(&value, f)?; - }} - } else { - quote! { core::fmt::Pointer::fmt(#expr, f)?; } - } - } + } => format_pointer(expr, pointer_value, alternate), TemplateFormatter::Binary { alternate - } => { - if alternate { - quote! { write!(f, "{:#b}", #expr)?; } - } else { - quote! { core::fmt::Binary::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "Binary", 'b', alternate), TemplateFormatter::Octal { alternate - } => { - if alternate { - quote! { write!(f, "{:#o}", #expr)?; } - } else { - quote! { core::fmt::Octal::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "Octal", 'o', alternate), TemplateFormatter::LowerExp { alternate - } => { - if alternate { - quote! { write!(f, "{:#e}", #expr)?; } - } else { - quote! { core::fmt::LowerExp::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "LowerExp", 'e', alternate), TemplateFormatter::UpperExp { alternate - } => { - if alternate { - quote! { write!(f, "{:#E}", #expr)?; } - } else { - quote! { core::fmt::UpperExp::fmt(#expr, f)?; } - } - } + } => format_with_optional_alternate(expr, "UpperExp", 'E', alternate) + } +} + +fn format_with_trait(expr: TokenStream, trait_name: &str) -> TokenStream { + let trait_ident = format_ident!("{}", trait_name); + quote! { + ::core::fmt::#trait_ident::fmt(#expr, f)?; + } +} + +fn format_with_optional_alternate( + expr: TokenStream, + trait_name: &str, + specifier: char, + alternate: bool +) -> TokenStream { + if alternate { + format_with_alternate(expr, specifier) + } else { + format_with_trait(expr, trait_name) + } +} + +fn format_with_alternate(expr: TokenStream, specifier: char) -> TokenStream { + let format_string = format!("{{:#{}}}", specifier); + quote! { + ::core::write!(f, #format_string, #expr)?; + } +} + +fn format_pointer(expr: TokenStream, pointer_value: bool, alternate: bool) -> TokenStream { + if alternate { + format_with_alternate(expr, 'p') + } else if pointer_value { + quote! {{ + let value = #expr; + ::core::fmt::Pointer::fmt(&value, f)?; + }} + } else { + format_with_trait(expr, "Pointer") } }