diff options
Diffstat (limited to 'mingling_macros/src/lib.rs')
| -rw-r--r-- | mingling_macros/src/lib.rs | 124 |
1 files changed, 92 insertions, 32 deletions
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index ae874af..ce3455e 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -36,7 +36,7 @@ //! │ V │ //! │ Reads all registries → generates ThisProgram with: │ //! │ • ProgramCollect impl (dispatch/render/chain dispatch tree) │ -//! │ • Fallback types (ErrorDispatcherNotFound, etc.) │ +//! │ • Fallback types (EntryFallback, etc.) │ //! │ • Completion logic (if `comp` feature enabled) │ //! └──────────────────────────────────────────────────────────────────┘ //! ``` @@ -100,10 +100,10 @@ //! |---------|---------------| //! | `clap` | `dispatcher_clap!` | //! | `comp` | [`#[completion]`](attr.completion.html), `suggest!`, `suggest_enum!` | -//! | `extra_macros` | `entry!`, `empty_result!`, `route!`, [`#[program_setup]`](attr.program_setup.html), `group!` | +//! | `extras` | `entry!`, `empty_result!`, `route!`, [`#[program_setup]`](attr.program_setup.html), `group!` | //! | `dispatch_tree` | `register_dispatcher!` (enables trie-based command dispatch) | //! | `structural_renderer` | `#[derive(StructuralData)]`, `pack_structural!`, `pack_err_structural!`, `group_structural!` | -//! | `structural_renderer` + `extra_macros` | `group_structural!`, `pack_err_structural!` | +//! | `structural_renderer` + `extras` | `group_structural!`, `pack_err_structural!` | //! | `async` | Enables async `#[chain]` functions | //! | `repl` | Enables REPL execution loop | //! @@ -120,8 +120,8 @@ //! ```rust,ignore //! // Example of what gen_program! generates (simplified): //! impl ProgramCollect for ThisProgram { -//! fn build_dispatcher_not_found(args: Vec<String>) -> AnyOutput { -//! AnyOutput::new(ErrorDispatcherNotFound::new(args)) +//! fn build_entry_fallback(args: Vec<String>) -> AnyOutput { +//! AnyOutput::new(EntryFallback::new(args)) //! } //! fn has_chain(any: &AnyOutput) -> bool { //! match any.member_id() { @@ -163,15 +163,15 @@ mod utils; use attr::completion; #[cfg(feature = "clap")] use attr::dispatcher_clap; -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] use attr::program_setup; -use attr::{chain, help, renderer}; +use attr::{chain, help, metadata, renderer}; use derive::{enum_tag, grouped}; -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] use func::entry; -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] pub(crate) use func::group as group_impl; -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] use func::pack_err; #[cfg(feature = "comp")] use func::suggest; @@ -209,6 +209,7 @@ pub(crate) static RENDERERS: Registry = OnceLock::new(); pub(crate) static CHAINS_EXIST: Registry = OnceLock::new(); pub(crate) static RENDERERS_EXIST: Registry = OnceLock::new(); pub(crate) static HELP_REQUESTS: Registry = OnceLock::new(); +pub(crate) static METADATA: Registry = OnceLock::new(); /// Checks if a variant name already exists in a registered set. /// Returns a `compile_error` token stream if a duplicate is found. @@ -293,7 +294,7 @@ fn entry_has_variant(entry: &str, variant_name: &str) -> bool { /// /// - The type must be accessible at the call site (imported or fully qualified). /// - The alias name (if provided) must not conflict with existing types. -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro] pub fn group(input: TokenStream) -> TokenStream { group_impl::group_macro(input) @@ -309,8 +310,8 @@ pub fn group(input: TokenStream) -> TokenStream { /// group_structural!(IoError = std::io::Error); /// ``` /// -/// Requires the `structural_renderer` and `extra_macros` features. -#[cfg(all(feature = "structural_renderer", feature = "extra_macros"))] +/// Requires the `structural_renderer` and `extras` features. +#[cfg(all(feature = "structural_renderer", feature = "extras"))] #[proc_macro] pub fn group_structural(input: TokenStream) -> TokenStream { func::group_structural::group_structural(input) @@ -490,8 +491,8 @@ pub fn pack_structural(input: TokenStream) -> TokenStream { /// When the `structural_renderer` feature is enabled, the struct also gets /// `#[derive(serde::Serialize)]`. /// -/// This macro is only available with the `extra_macros` feature. -#[cfg(feature = "extra_macros")] +/// This macro is only available with the `extras` feature. +#[cfg(feature = "extras")] #[proc_macro] pub fn pack_err(input: TokenStream) -> TokenStream { pack_err::pack_err(input) @@ -507,8 +508,8 @@ pub fn pack_err(input: TokenStream) -> TokenStream { /// pack_err_structural!(ErrorNotDir = PathBuf); /// ``` /// -/// Requires the `structural_renderer` and `extra_macros` features. -#[cfg(all(feature = "structural_renderer", feature = "extra_macros"))] +/// Requires the `structural_renderer` and `extras` features. +#[cfg(all(feature = "structural_renderer", feature = "extras"))] #[proc_macro] pub fn pack_err_structural(input: TokenStream) -> TokenStream { func::pack_err_structural::pack_err_structural(input) @@ -568,7 +569,7 @@ pub fn pack_err_structural(input: TokenStream) -> TokenStream { /// value.to_chain() /// } /// ``` -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro] pub fn route(input: TokenStream) -> TokenStream { func::route::route(input) @@ -611,7 +612,7 @@ pub fn route(input: TokenStream) -> TokenStream { /// Ok(RenderResult::new()) /// } /// ``` -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro] pub fn render_route(input: TokenStream) -> TokenStream { func::render_route::render_route(input) @@ -665,7 +666,7 @@ pub fn render_route(input: TokenStream) -> TokenStream { /// /// [`ResultEmpty`]: https://docs.rs/mingling/latest/mingling/type.ResultEmpty.html /// [`ChainProcess`]: https://docs.rs/mingling/latest/mingling/enum.ChainProcess.html -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro] pub fn empty_result(input: TokenStream) -> TokenStream { func::empty_result::empty_result(input) @@ -692,9 +693,9 @@ pub fn empty_result(input: TokenStream) -> TokenStream { /// dispatcher!(MyProgram, "command.path", CommandStruct => EntryStruct); /// ``` /// -/// ## Abbreviated syntax (requires `extra_macros` feature) +/// ## Abbreviated syntax (requires `extras` feature) /// -/// When the `extra_macros` feature is enabled, the `CommandStruct => EntryStruct` +/// When the `extras` feature is enabled, the `CommandStruct => EntryStruct` /// portion can be omitted. Struct names are auto-derived from the command path /// using `PascalCase` conversion: /// @@ -721,7 +722,7 @@ pub fn empty_result(input: TokenStream) -> TokenStream { /// // With explicit program: /// dispatcher!(MyApp, "status", StatusCommand => StatusEntry); /// -/// // Abbreviated form (extra_macros required): +/// // Abbreviated form (extras required): /// // dispatcher!("remote.add"); // → CMDRemoteAdd, EntryRemoteAdd /// ``` /// @@ -978,11 +979,11 @@ pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream { /// The macros `gen_program!` automatically generates two fallback types that /// you can provide renderers for: /// - `ErrorRendererNotFound` — triggered when no matching renderer is found -/// - `ErrorDispatcherNotFound` — triggered when no matching dispatcher is found +/// - `EntryFallback` — triggered when no matching dispatcher is found /// /// ```rust,ignore /// #[renderer] -/// fn fallback_dispatcher_not_found(prev: ErrorDispatcherNotFound) -> RenderResult { +/// fn fallback_dispatcher_not_found(prev: EntryFallback) -> RenderResult { /// let mut result = RenderResult::new(); /// writeln!(result, "Unknown command: {}", prev.join(", ")); /// result @@ -1094,7 +1095,7 @@ pub fn completion(attr: TokenStream, item: TokenStream) -> TokenStream { /// - The function must have exactly one parameter of type `&mut Program<G>`. /// - The function must return `()`. /// - The function cannot be async. -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro_attribute] pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { program_setup::setup_attr(attr, item) @@ -1102,7 +1103,7 @@ pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { /// Declares a command from a plain function. /// -/// **This macro is only available with the `extra_macros` feature.** +/// **This macro is only available with the `extras` feature.** /// /// The `#[command]` attribute converts a function taking `Vec<String>` into a /// Mingling command by: @@ -1181,7 +1182,7 @@ pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { /// - The function must have at least one parameter (the `Vec<String>` entry argument). /// - The function must not have a `self` parameter. /// - Visibility (`pub` etc.) and `async` are preserved on the original function. -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro_attribute] pub fn command(attr: TokenStream, item: TokenStream) -> TokenStream { attr::command::command_attr(attr, item) @@ -1273,7 +1274,7 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// - `pack!` — For creating the wrapper types used with `entry!`. /// - `dispatcher!` — Which implicitly creates entry types via `pack!`. -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro] pub fn entry(input: TokenStream) -> TokenStream { entry::entry(input) @@ -1297,6 +1298,26 @@ pub fn register_help(input: TokenStream) -> TokenStream { func::register_help::register_help(input) } +/// Registers metadata mapping between an enum variant and a metadata type. +/// +/// This macro is used internally by the `#[metadata]` attribute and is also +/// available for manual registration if needed. +/// +/// # Syntax +/// +/// ```rust,ignore +/// register_metadata!(EntryVariant, MetadataType); +/// ``` +/// +/// This adds an entry to the global `METADATA` registry, mapping the enum +/// variant for `EntryVariant` to the metadata provider trait +/// `::mingling::Metadata<MetadataType>`. The entry is consumed by +/// `gen_program!` to generate the `get_metadata` method of `ProgramCollect`. +#[proc_macro] +pub fn register_metadata(input: TokenStream) -> TokenStream { + func::register_metadata::register_metadata_impl(input) +} + /// Registers a dispatcher at compile time for the `dispatch_tree` feature. /// /// This macro is called internally by `dispatcher!` when the `dispatch_tree` @@ -1404,6 +1425,45 @@ pub fn help(attr: TokenStream, item: TokenStream) -> TokenStream { help::help_attr(item) } +/// Declares compile-time metadata for an entry variant. +/// +/// The `#[metadata]` attribute attaches an arbitrary, compile-time-typed value +/// to an entry. The annotated function becomes the provider for the metadata: +/// its return type is the metadata type, and the attribute argument names the +/// entry enum variant the metadata belongs to. +/// +/// The macro works by: +/// 1. Generating `impl ::mingling::Metadata<ReturnType> for EntryVariant` whose +/// `init_metadata()` calls the annotated function. +/// 2. Registering the entry via `register_metadata!` in the global `METADATA` +/// registry so that `gen_program!` emits the `get_metadata` method. +/// 3. Keeping the original function unchanged for direct calls. +/// +/// # Syntax +/// +/// ```rust,ignore +/// #[metadata(EntryGreet)] +/// fn greet_desc() -> Description { +/// Description { desc: "ok".into() } +/// } +/// ``` +/// +/// The metadata is later retrieved with `ProgramCollect::get_metadata`: +/// +/// ```rust,ignore +/// let desc = ThisProgram::get_metadata::<Description>(ThisProgram::EntryGreet); +/// ``` +/// +/// # Requirements +/// +/// - The attribute argument must be the enum variant to attach metadata to. +/// - The function must take no parameters and return a concrete type. +/// - The function cannot be async. +#[proc_macro_attribute] +pub fn metadata(attr: TokenStream, item: TokenStream) -> TokenStream { + metadata::metadata_attr(attr, item) +} + /// Marker attribute for the Mingling lint system. /// /// The content of this attribute is ignored by rustc and reserved for @@ -1438,7 +1498,7 @@ pub fn mlint(attr: TokenStream, item: TokenStream) -> TokenStream { /// StateCalculate { number_a: a, operator: op, ... }.to_chain() /// } /// ``` -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro_attribute] pub fn routeify(attr: TokenStream, item: TokenStream) -> TokenStream { extensions::routeify::routeify_impl(attr, item) @@ -1464,7 +1524,7 @@ pub fn routeify(attr: TokenStream, item: TokenStream) -> TokenStream { /// Ok(RenderResult::new()) /// } /// ``` -#[cfg(feature = "extra_macros")] +#[cfg(feature = "extras")] #[proc_macro_attribute] pub fn renderify(attr: TokenStream, item: TokenStream) -> TokenStream { extensions::renderify::renderify_impl(attr, item) @@ -1819,7 +1879,7 @@ pub fn derive_grouped_serialize(input: TokenStream) -> TokenStream { /// 1. **`pub type Next = ChainProcess<ProgramName>`** — A convenience type alias /// for use in chain function return types. /// 2. **`program_comp_gen!(...)`** (with `comp` feature) — Generates completion infrastructure. -/// 3. **`program_fallback_gen!(...)`** — Generates `ErrorRendererNotFound` and `ErrorDispatcherNotFound` types. +/// 3. **`program_fallback_gen!(...)`** — Generates `ErrorRendererNotFound` and `EntryFallback` types. /// 4. **`program_final_gen!(...)`** — Generates the program enum with: /// - An enum with all packed types as variants /// - `Display` implementation for the enum |
