aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-console-macros/src/lib.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-22 22:53:09 +0900
committernsfisis <nsfisis@gmail.com>2026-02-22 22:53:22 +0900
commit6f3802fd9f39c4e5847d130b4417b5cdfb66972d (patch)
tree166cca2cf0645d280bfa376a513a049c70241dea /crates/mozart-console-macros/src/lib.rs
parent1d33728151b282949e7e14646e722d7775de4453 (diff)
downloadphp-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.rs70
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,
+ })
+ }
+}