diff options
Diffstat (limited to 'mingling_macros')
| -rw-r--r-- | mingling_macros/src/extensions.rs | 46 | ||||
| -rw-r--r-- | mingling_macros/src/extensions/routeify.rs | 39 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 21 |
3 files changed, 66 insertions, 40 deletions
diff --git a/mingling_macros/src/extensions.rs b/mingling_macros/src/extensions.rs index 97ef636..aff20e2 100644 --- a/mingling_macros/src/extensions.rs +++ b/mingling_macros/src/extensions.rs @@ -3,38 +3,18 @@ //! This module provides a way for attribute macros like `#[chain]`, `#[renderer]`, //! `#[help]`, and `#[completion]` to accept extension identifiers that are //! applied as outer attributes before the bare macro. -//! -//! # How it works -//! -//! When a user writes `#[chain(routeify, other_ext)]`, the extension point mechanism -//! detects `routeify` and `other_ext` as extension identifiers, and generates a -//! re-dispatch token stream equivalent to: -//! -//! ```ignore -//! #[other_ext] -//! #[routeify] -//! #[chain] -//! fn handle(...) { ... } -//! ``` -//! -//! The original bare macro (`#[chain]` without extensions) sees no arguments -//! and proceeds as normal — the extensions have already been stripped. use proc_macro::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::{Ident, Token}; +/// Extension: `#[routeify]` — transforms `expr?` into `route!(expr)`. +#[cfg(feature = "extra_macros")] +pub(crate) mod routeify; + /// Parsed extensions from an attribute macro like `#[chain(routeify, other_ext)]`. -/// -/// For macros with no fixed arguments (chain, renderer, help): -/// - `exts` contains all parsed extension identifiers -/// - `fixed` is empty -/// -/// For macros with fixed arguments (completion): -/// - Use `parse_completion_attr_extensions` instead. pub(crate) struct Extensions { - /// Extension identifiers (e.g., `routeify`, `other_ext`) pub(crate) exts: Vec<Ident>, } @@ -53,23 +33,16 @@ impl Parse for Extensions { } /// Parsed extensions for `#[completion(EntryType, routeify, ...)]`. -/// -/// The first argument is the entry type path, the rest are extension identifiers. #[cfg(feature = "comp")] pub(crate) struct CompletionExt { - /// The entry type path (first argument, e.g., `HelloEntry` or `crate::Entry`) pub(crate) entry_type: proc_macro2::TokenStream, - /// Extension identifiers (e.g., `routeify`, `other_ext`) pub(crate) exts: Vec<Ident>, } #[cfg(feature = "comp")] impl Parse for CompletionExt { fn parse(input: ParseStream) -> syn::Result<Self> { - // Parse the first argument as the entry type path let entry_type: proc_macro2::TokenStream = input.parse()?; - - // Parse remaining arguments as extensions let mut exts = Vec::new(); while !input.is_empty() { let _ = input.parse::<Token![,]>(); @@ -79,16 +52,12 @@ impl Parse for CompletionExt { let ident: Ident = input.parse()?; exts.push(ident); } - Ok(CompletionExt { entry_type, exts }) } } /// Generates a re-dispatch token stream for attribute macros that take **no fixed arguments** /// (chain, renderer, help). -/// -/// If `attr` contains extensions, the output is `#[ext1] #[ext2] ...[#bare_attr] #item`. -/// If `attr` is empty or contains no extensions, returns `None`. pub(crate) fn try_redispatch_simple( attr: TokenStream, item: &TokenStream, @@ -109,7 +78,7 @@ pub(crate) fn try_redispatch_simple( Some( quote! { - #(#exts)* + #(#[#exts])* #[#bare] #item } @@ -118,9 +87,6 @@ pub(crate) fn try_redispatch_simple( } /// Generates a re-dispatch token stream for `#[completion(EntryType, ...)]`. -/// -/// If extensions are found, the output is `#[ext1] #[ext2] #[completion(EntryType)] #item`. -/// If no extensions, returns `None`. #[cfg(feature = "comp")] pub(crate) fn try_redispatch_completion( attr: TokenStream, @@ -141,7 +107,7 @@ pub(crate) fn try_redispatch_completion( Some( quote! { - #(#exts)* + #(#[#exts])* #[::mingling::macros::completion(#entry_type)] #item } diff --git a/mingling_macros/src/extensions/routeify.rs b/mingling_macros/src/extensions/routeify.rs new file mode 100644 index 0000000..f011fb9 --- /dev/null +++ b/mingling_macros/src/extensions/routeify.rs @@ -0,0 +1,39 @@ +//! The `#[routeify]` extension — transforms `expr?` into `route!(expr)`. +//! +//! Designed as an extension for the Mingling attribute macro system, intended +//! to be used with `#[chain(routeify)]` or standalone as `#[routeify]`. +//! +//! # How it works +//! +//! The macro parses the function AST and replaces every `Expr::Try` node with an +//! equivalent `route!(expr)` invocation. + +use proc_macro::TokenStream; +use quote::ToTokens; +use syn::visit_mut::VisitMut; +use syn::{Expr, ItemFn, parse_macro_input}; + +struct RouteifyTransform; + +impl VisitMut for RouteifyTransform { + fn visit_expr_mut(&mut self, expr: &mut Expr) { + syn::visit_mut::visit_expr_mut(self, expr); + + if let Expr::Try(try_expr) = expr { + let inner = &*try_expr.expr; + let inner_tokens = inner.to_token_stream(); + + if let Ok(macro_expr) = syn::parse2::<Expr>(quote::quote! { + ::mingling::macros::route!(#inner_tokens) + }) { + *expr = macro_expr; + } + } + } +} + +pub(crate) fn routeify_impl(_attr: TokenStream, item: TokenStream) -> TokenStream { + let mut input_fn = parse_macro_input!(item as ItemFn); + RouteifyTransform.visit_item_fn_mut(&mut input_fn); + input_fn.to_token_stream().into() +} diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 3743df8..a85648f 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -1239,6 +1239,27 @@ pub fn help(attr: TokenStream, item: TokenStream) -> TokenStream { help::help_attr(item) } +/// Extension attribute macro that transforms `expr?` into `route!(expr)`. +/// +/// Designed for use with `#[chain(routeify, ...)]` to enable concise error +/// routing in chain functions using the `?` operator syntax. +/// +/// # Example +/// +/// ```rust,ignore +/// #[chain(routeify)] +/// fn handle_calc(args: EntryCalculate) -> Next { +/// let a = args.pick(&arg![f32]).to_result()?; +/// let op = args.pick(&arg![Operator]).to_result()?; +/// StateCalculate { number_a: a, operator: op, ... }.to_chain() +/// } +/// ``` +#[cfg(feature = "extra_macros")] +#[proc_macro_attribute] +pub fn routeify(attr: TokenStream, item: TokenStream) -> TokenStream { + extensions::routeify::routeify_impl(attr, item) +} + /// Derive macro for automatically implementing the `Grouped` trait on a struct. /// /// The `#[derive(Grouped)]` macro: |
