diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-22 22:53:09 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-22 22:53:22 +0900 |
| commit | 6f3802fd9f39c4e5847d130b4417b5cdfb66972d (patch) | |
| tree | 166cca2cf0645d280bfa376a513a049c70241dea /crates/mozart-console-macros/src/lib.rs | |
| parent | 1d33728151b282949e7e14646e722d7775de4453 (diff) | |
| download | php-mozart-6f3802fd9f39c4e5847d130b4417b5cdfb66972d.tar.gz php-mozart-6f3802fd9f39c4e5847d130b4417b5cdfb66972d.tar.zst php-mozart-6f3802fd9f39c4e5847d130b4417b5cdfb66972d.zip | |
refactor(console): add console_format! proc macro and migrate all commands
Introduce a Symfony Console-style tag macro that replaces verbose
patterns like `console::info(&format!("text {name}"))` with
`console_format!("<info>text {name}</info>")`. Supports all 6 tag
types (info, comment, error, question, highlight, warning) with
format argument distribution across multiple tagged segments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-console-macros/src/lib.rs')
| -rw-r--r-- | crates/mozart-console-macros/src/lib.rs | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/crates/mozart-console-macros/src/lib.rs b/crates/mozart-console-macros/src/lib.rs new file mode 100644 index 0000000..3af6f82 --- /dev/null +++ b/crates/mozart-console-macros/src/lib.rs @@ -0,0 +1,70 @@ +mod codegen; +mod parser; + +use proc_macro::TokenStream; +use syn::Expr; +use syn::punctuated::Punctuated; + +/// Format a string with Symfony Console-style tags. +/// +/// Supported tags: `<info>`, `<comment>`, `<error>`, `<question>`, `<highlight>`, `<warning>`. +/// +/// # Examples +/// +/// ```ignore +/// // Single tagged segment +/// console_format!("<info>All packages are up to date.</info>") +/// +/// // With format arguments +/// console_format!("<info>Removing {name} from require-dev</info>") +/// +/// // Mixed tags +/// console_format!("<info>{}</info> : <comment>{}</comment>", label, value) +/// +/// // Plain text (equivalent to format!) +/// console_format!("plain text {}", x) +/// ``` +#[proc_macro] +pub fn console_format(input: TokenStream) -> TokenStream { + let input2: proc_macro2::TokenStream = input.into(); + match console_format_impl(input2) { + Ok(tokens) => tokens.into(), + Err(err) => err.into_compile_error().into(), + } +} + +fn console_format_impl( + input: proc_macro2::TokenStream, +) -> Result<proc_macro2::TokenStream, syn::Error> { + let args: ConsoleFormatArgs = syn::parse2(input)?; + let segments = parser::parse_format_string(&args.format_str) + .map_err(|msg| syn::Error::new(args.format_str_span, msg))?; + Ok(codegen::generate(&segments, &args.extra_args)) +} + +struct ConsoleFormatArgs { + format_str: String, + format_str_span: proc_macro2::Span, + extra_args: Punctuated<Expr, syn::Token![,]>, +} + +impl syn::parse::Parse for ConsoleFormatArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + let lit: syn::LitStr = input.parse()?; + let format_str = lit.value(); + let format_str_span = lit.span(); + + let extra_args = if input.peek(syn::Token![,]) { + input.parse::<syn::Token![,]>()?; + Punctuated::parse_terminated(input)? + } else { + Punctuated::new() + }; + + Ok(ConsoleFormatArgs { + format_str, + format_str_span, + extra_args, + }) + } +} |
