diff --git a/CHANGELOG.md b/CHANGELOG.md index 55d1a36..1effc6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ All notable changes to this project will be documented in this file. ### Added - _Nothing yet._ +## [0.5.14] - 2025-10-06 + +### Added +- Prepared the derive input structures for future `format_args!` support by + introducing display specification variants for templates with arguments and + `fmt = ` handlers, along with `FormatArgsSpec`/`FormatArg` metadata + scaffolding. + ## [0.5.13] - 2025-10-05 ### Documentation diff --git a/Cargo.lock b/Cargo.lock index 9d969e3..5ab7d3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,7 +1527,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.5.13" +version = "0.5.14" dependencies = [ "actix-web", "axum", @@ -1557,7 +1557,7 @@ dependencies = [ [[package]] name = "masterror-derive" -version = "0.1.5" +version = "0.1.6" dependencies = [ "masterror-template", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index a23bf9f..f0e40c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.5.13" +version = "0.5.14" 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.5", path = "masterror-derive" } +masterror-derive = { version = "0.1.6", path = "masterror-derive" } masterror-template = { version = "0.1.4", path = "masterror-template" } [dependencies] diff --git a/README.md b/README.md index 4c7ddd3..ad01f98 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Stable categories, conservative HTTP mapping, no `unsafe`. ~~~toml [dependencies] -masterror = { version = "0.5.13", default-features = false } +masterror = { version = "0.5.14", default-features = false } # or with features: -# masterror = { version = "0.5.13", features = [ +# masterror = { version = "0.5.14", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -66,10 +66,10 @@ masterror = { version = "0.5.13", default-features = false } ~~~toml [dependencies] # lean core -masterror = { version = "0.5.13", default-features = false } +masterror = { version = "0.5.14", default-features = false } # with Axum/Actix + JSON + integrations -# masterror = { version = "0.5.13", features = [ +# masterror = { version = "0.5.14", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -383,13 +383,13 @@ assert_eq!(resp.status, 401); Minimal core: ~~~toml -masterror = { version = "0.5.13", default-features = false } +masterror = { version = "0.5.14", default-features = false } ~~~ API (Axum + JSON + deps): ~~~toml -masterror = { version = "0.5.13", features = [ +masterror = { version = "0.5.14", features = [ "axum", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } @@ -398,7 +398,7 @@ masterror = { version = "0.5.13", features = [ API (Actix + JSON + deps): ~~~toml -masterror = { version = "0.5.13", features = [ +masterror = { version = "0.5.14", features = [ "actix", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } diff --git a/README.ru.md b/README.ru.md index 521a5ac..77d308e 100644 --- a/README.ru.md +++ b/README.ru.md @@ -27,9 +27,9 @@ ~~~toml [dependencies] -masterror = { version = "0.5.13", default-features = false } +masterror = { version = "0.5.14", default-features = false } # или с нужными интеграциями -# masterror = { version = "0.5.13", features = [ +# masterror = { version = "0.5.14", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", diff --git a/masterror-derive/Cargo.toml b/masterror-derive/Cargo.toml index a2168fe..61e72bd 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.5" +version = "0.1.6" 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 69acce4..bc4f641 100644 --- a/masterror-derive/src/display.rs +++ b/masterror-derive/src/display.rs @@ -1,7 +1,7 @@ use masterror_template::template::{TemplateFormatter, TemplateFormatterKind}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -use syn::Error; +use syn::{Error, spanned::Spanned}; use crate::{ input::{ @@ -25,9 +25,17 @@ fn expand_struct(input: &ErrorInput, data: &StructData) -> Result render_struct_transparent(&data.fields), - DisplaySpec::Template(template) => render_template(template, |placeholder| { + DisplaySpec::Template(template) + | DisplaySpec::TemplateWithArgs { + template, .. + } => render_template(template, |placeholder| { struct_placeholder_expr(&data.fields, placeholder) - })? + })?, + DisplaySpec::FormatterPath { + path, .. + } => { + return Err(Error::new(path.span(), "`fmt = ...` is not supported yet")); + } }; let ident = &input.ident; @@ -81,7 +89,13 @@ fn render_variant(variant: &VariantData) -> Result { DisplaySpec::Transparent { .. } => render_variant_transparent(variant), - DisplaySpec::Template(template) => render_variant_template(variant, template) + DisplaySpec::Template(template) + | DisplaySpec::TemplateWithArgs { + template, .. + } => render_variant_template(variant, template), + DisplaySpec::FormatterPath { + path, .. + } => Err(Error::new(path.span(), "`fmt = ...` is not supported yet")) } } diff --git a/masterror-derive/src/error_trait.rs b/masterror-derive/src/error_trait.rs index ec07553..f4d0edb 100644 --- a/masterror-derive/src/error_trait.rs +++ b/masterror-derive/src/error_trait.rs @@ -81,7 +81,13 @@ fn struct_source_body(fields: &Fields, display: &DisplaySpec) -> TokenStream { quote! { None } } } - DisplaySpec::Template(_) => { + DisplaySpec::Template(_) + | DisplaySpec::TemplateWithArgs { + .. + } + | DisplaySpec::FormatterPath { + .. + } => { if let Some(field) = fields.iter().find(|field| field.attrs.has_source()) { let member = &field.member; field_source_expr(quote!(self.#member), quote!(&self.#member), &field.ty) @@ -97,7 +103,13 @@ fn variant_source_arm(variant: &VariantData) -> TokenStream { DisplaySpec::Transparent { .. } => variant_transparent_source(variant), - DisplaySpec::Template(_) => variant_template_source(variant) + DisplaySpec::Template(_) + | DisplaySpec::TemplateWithArgs { + .. + } + | DisplaySpec::FormatterPath { + .. + } => variant_template_source(variant) } } diff --git a/masterror-derive/src/input.rs b/masterror-derive/src/input.rs index ffa305e..0b47449 100644 --- a/masterror-derive/src/input.rs +++ b/masterror-derive/src/input.rs @@ -1,7 +1,7 @@ -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use syn::{ - Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Field as SynField, - Fields as SynFields, GenericArgument, Ident, LitStr, spanned::Spanned + Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Expr, Field as SynField, + Fields as SynFields, GenericArgument, Ident, LitStr, Path, spanned::Spanned }; use crate::template_support::{DisplayTemplate, TemplateIdentifierSpec, parse_display_template}; @@ -21,16 +21,20 @@ pub enum ErrorData { #[derive(Debug)] pub struct StructData { - pub fields: Fields, - pub display: DisplaySpec + pub fields: Fields, + pub display: DisplaySpec, + #[allow(dead_code)] + pub format_args: FormatArgsSpec } #[derive(Debug)] pub struct VariantData { - pub ident: Ident, - pub fields: Fields, - pub display: DisplaySpec, - pub span: Span + pub ident: Ident, + pub fields: Fields, + pub display: DisplaySpec, + #[allow(dead_code)] + pub format_args: FormatArgsSpec, + pub span: Span } #[derive(Debug)] @@ -245,8 +249,43 @@ impl FieldAttrs { #[derive(Debug)] pub enum DisplaySpec { - Transparent { attribute: Box }, - Template(DisplayTemplate) + Transparent { + attribute: Box + }, + Template(DisplayTemplate), + #[allow(dead_code)] + TemplateWithArgs { + template: DisplayTemplate, + args: FormatArgsSpec + }, + #[allow(dead_code)] + FormatterPath { + path: Path, + args: FormatArgsSpec + } +} + +#[allow(dead_code)] +#[derive(Debug, Default)] +pub struct FormatArgsSpec { + pub args: Vec +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct FormatArg { + pub tokens: TokenStream, + pub expr: Expr, + pub kind: FormatBindingKind, + pub span: Span +} + +#[allow(dead_code)] +#[derive(Debug)] +pub enum FormatBindingKind { + Named(Ident), + Positional(usize), + Implicit } pub fn parse_input(input: DeriveInput) -> Result { @@ -300,7 +339,8 @@ fn parse_struct( Ok(ErrorData::Struct(Box::new(StructData { fields, - display + display, + format_args: FormatArgsSpec::default() }))) } @@ -348,6 +388,7 @@ fn parse_variant(variant: syn::Variant, errors: &mut Vec) -> Result