diff options
Diffstat (limited to 'mingling_macros/src/lib.rs')
| -rw-r--r-- | mingling_macros/src/lib.rs | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 462c6dd..ca3261e 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -141,6 +141,8 @@ //! } //! ``` +#![deny(missing_docs)] + #[cfg(feature = "extra_macros")] use quote::quote; @@ -270,6 +272,36 @@ fn entry_has_variant(entry: &str, variant_name: &str) -> bool { entry.contains(&format!(":: {variant_name} =>")) } +/// Registers an outside type (from `std` or other crates) as a type recognizable +/// by the Mingling framework, without modifying the original type definition. +/// +/// This macro generates a newtype wrapper around the given type that implements +/// `Grouped`, `Into<AnyOutput>`, `Into<ChainProcess>`, and the `Routable` trait, +/// making the outside type usable in `#[chain]` and `#[renderer]` functions. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Simple form — creates a wrapper named after the type's last segment: +/// group!(ParseIntError); +/// +/// // Aliased form — creates a wrapper with a custom name: +/// group!(ErrorIo = std::io::Error); +/// ``` +/// +/// # Example +/// +/// See the full example in the crate documentation or run: +/// ```bash +/// cargo run --example example-outside-type -- parse 42 +/// cargo run --example example-outside-type -- parse hello +/// cargo run --example example-outside-type -- error +/// ``` +/// +/// # Requirements +/// +/// - 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")] #[proc_macro] pub fn group(input: TokenStream) -> TokenStream { @@ -558,6 +590,56 @@ pub fn route(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// Routes errors to the rendering pipeline instead of the chain pipeline. +/// +/// This macro is similar to [`route!`] but instead of routing errors through +/// `Routable::to_chain()` (which returns `ChainProcess`), it routes them +/// directly to the renderer via `crate::ThisProgram::render(AnyOutput::new(e))` +/// (which returns `RenderResult`). +/// +/// This is useful in `#[renderer]` and `#[help]` functions where the return +/// type is `RenderResult` rather than `ChainProcess`. +/// +/// # Syntax +/// +/// ```rust,ignore +/// render_route!(expr) +/// ``` +/// +/// Where `expr` is an expression of type `Result<T, E>`. +/// +/// # Interaction with `#[routeify]` +/// +/// When `#[routeify]` is used on a `#[renderer]` or `#[help]` function (e.g. +/// `#[renderer(routeify)]` or `#[help(routeify)]`), every `expr?` is automatically +/// replaced with `render_route!(expr)` instead of `route!(expr)`. +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{renderer, render_route}; +/// use std::io::Write; +/// +/// #[renderer] +/// fn render_something(prev: SomeType) -> RenderResult { +/// let data = render_route!(fetch_data().map_err(|e| ErrorEntry::new(e.to_string())))?; +/// // ... render data +/// Ok(RenderResult::new()) +/// } +/// ``` +#[cfg(feature = "extra_macros")] +#[proc_macro] +pub fn render_route(input: TokenStream) -> TokenStream { + let expr = parse_macro_input!(input as syn::Expr); + let expanded = quote! { + match #expr { + Ok(r) => r, + Err(e) => return <crate::ThisProgram as ::mingling::ProgramCollect>::render(::mingling::AnyOutput::new(e)), + } + }; + TokenStream::from(expanded) +} + /// Creates an empty result value wrapped in `ChainProcess` for early return /// from a chain function. /// @@ -1261,6 +1343,25 @@ pub fn help(attr: TokenStream, item: TokenStream) -> TokenStream { help::help_attr(item) } +/// Marker attribute for the Mingling lint system. +/// +/// The content of this attribute is ignored by rustc and reserved for +/// the mlint tool to interpret. All it does is pass the item through +/// unchanged. +/// +/// # Examples +/// +/// ```rust,ignore +/// #[mlint(allow(MLINT_SOME_LINT))] +/// #[mlint(warn(MLINT_SOME_LINT))] +/// #[mlint(deny(MLINT_SOME_LINT))] +/// fn some_item() {} +/// ``` +#[proc_macro_attribute] +pub fn mlint(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} + /// Extension attribute macro that transforms `expr?` into `route!(expr)`. /// /// Designed for use with `#[chain(routeify, ...)]` to enable concise error @@ -1282,6 +1383,226 @@ pub fn routeify(attr: TokenStream, item: TokenStream) -> TokenStream { extensions::routeify::routeify_impl(attr, item) } +/// Extension attribute macro that transforms `expr?` into `render_route!(expr)`. +/// +/// Designed for use with `#[renderer(renderify, ...)]` or `#[help(renderify, ...)]` +/// to enable concise error routing in renderer and help functions using the `?` +/// operator syntax. +/// +/// Unlike `#[routeify]` which routes errors to the chain pipeline via `route!`, +/// `#[renderify]` routes errors to the rendering pipeline via `render_route!`, +/// which matches the `RenderResult` return type of renderer and help functions. +/// +/// # Example +/// +/// ```rust,ignore +/// #[renderer(renderify)] +/// fn render_greeting(prev: Greeting) -> RenderResult { +/// let data = load_data()?; // expands to render_route!(load_data()) +/// r_println!("{data}"); +/// Ok(RenderResult::new()) +/// } +/// ``` +#[cfg(feature = "extra_macros")] +#[proc_macro_attribute] +pub fn renderify(attr: TokenStream, item: TokenStream) -> TokenStream { + extensions::renderify::renderify_impl(attr, item) +} + +/// Wraps a unit-returning function to produce a `RenderResult`. +/// +/// The `#[buffer]` attribute macro injects a local `__render_result_buffer` +/// variable of type `::mingling::RenderResult` and changes the function's +/// return type to `::mingling::RenderResult`. Inside the body, use the +/// `r_print!` and `r_println!` macros to write into the buffer. +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_println}; +/// +/// #[buffer] +/// fn render_my_type(prev: MyType) { +/// r_println!("Value: {:?}", *prev); +/// } +/// ``` +/// +/// This expands to: +/// +/// ```rust,ignore +/// fn render_my_type(prev: MyType) -> mingling::RenderResult { +/// let mut __render_result_buffer = mingling::RenderResult::new(); +/// { +/// r_println!("Value: {:?}", *prev); +/// } +/// __render_result_buffer +/// } +/// ``` +/// +/// # Requirements +/// +/// - The function must return `()` (unit). +/// - The function cannot be async. +#[proc_macro_attribute] +pub fn buffer(attr: TokenStream, item: TokenStream) -> TokenStream { + extensions::buffer::buffer_impl(attr, item) +} + +/// Prints text to a `RenderResult` buffer, with a trailing newline. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_println}; +/// +/// #[buffer] +/// fn render() { +/// r_println!("Hello, {}!", name); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// Pass a `RenderResult` variable as the first argument: +/// +/// ```rust,ignore +/// use mingling::macros::r_println; +/// use mingling::RenderResult; +/// +/// let mut r = RenderResult::new(); +/// r_println!(r, "value: {}", 42); +/// assert_eq!(&*r, "value: 42\n"); +/// ``` +#[proc_macro] +pub fn r_println(input: TokenStream) -> TokenStream { + func::r_print::r_println(input) +} + +/// Prints text to a `RenderResult` buffer, without a trailing newline. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_print}; +/// +/// #[buffer] +/// fn render() { +/// r_print!("Hello, "); +/// r_println!("world!"); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// ```rust,ignore +/// use mingling::macros::r_print; +/// use mingling::RenderResult; +/// +/// let mut r = RenderResult::new(); +/// r_print!(r, "value: {}", 42); +/// assert_eq!(&*r, "value: 42"); +/// ``` +#[proc_macro] +pub fn r_print(input: TokenStream) -> TokenStream { + func::r_print::r_print(input) +} + +/// Prints text to a `RenderResult` buffer (standard error style), with a trailing newline. +/// +/// This macro works identically to `r_println!` but conceptually targets +/// "error output" — it writes into a `RenderResult` buffer with a trailing newline. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_eprintln}; +/// +/// #[buffer] +/// fn render() { +/// r_eprintln!("Error: {}", err_msg); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// Pass a `RenderResult` variable as the first argument: +/// +/// ```rust,ignore +/// use mingling::macros::r_eprintln; +/// use mingling::RenderResult; +/// +/// let mut r = RenderResult::new(); +/// r_eprintln!(r, "error: {}", 42); +/// assert_eq!(&*r, "error: 42\n"); +/// ``` +#[proc_macro] +pub fn r_eprintln(input: TokenStream) -> TokenStream { + func::r_print::r_eprintln(input) +} + +/// Prints text to a `RenderResult` buffer (standard error style), without a trailing newline. +/// +/// This macro works identically to `r_print!` but conceptually targets +/// "error output" — it writes into a `RenderResult` buffer without a trailing newline. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_eprint}; +/// +/// #[buffer] +/// fn render() { +/// r_eprint!("Error: "); +/// r_eprintln!("something went wrong"); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// ```rust,ignore +/// use mingling::macros::r_eprint; +/// use mingling::RenderResult; +/// +/// let mut r = RenderResult::new(); +/// r_eprint!(r, "error: "); +/// r_eprintln!(r, "42"); +/// assert_eq!(&*r, "error: 42\n"); +/// ``` +#[proc_macro] +pub fn r_eprint(input: TokenStream) -> TokenStream { + func::r_print::r_eprint(input) +} + +/// Appends the contents of one `RenderResult` to another. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_append}; +/// +/// #[buffer] +/// fn render() { +/// let other = make_other_result(); +/// r_append!(other); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// ```rust,ignore +/// use mingling::macros::r_append; +/// use mingling::RenderResult; +/// +/// let mut dst = RenderResult::new(); +/// let src = RenderResult::from("hello"); +/// r_append!(dst, src); +/// assert!(!dst.is_empty()); +/// ``` +#[proc_macro] +pub fn r_append(input: TokenStream) -> TokenStream { + func::r_print::r_append(input) +} + /// Derive macro for automatically implementing the `Grouped` trait on a struct. /// /// The `#[derive(Grouped)]` macro: @@ -1472,6 +1793,28 @@ pub fn gen_program(input: TokenStream) -> TokenStream { func::gen_program::gen_program_impl(input) } +/// Internal macro used by `gen_program!` to generate the completion infrastructure for +/// shell completion support. +/// +/// **This macro is only available with the `comp` feature.** +/// +/// The `program_comp_gen!` macro generates: +/// 1. A hidden `__internal_completion_mod` module containing: +/// - A `CompletionContext` packed type (wrapping `ShellContext`), dispatched via `"__comp"`. +/// - A `CompletionSuggest` packed type (wrapping `(ShellContext, Suggest)`). +/// - An internal dispatcher (`CMDCompletion`) for the `"__comp"` command path. +/// 2. An internal chain function `__exec_completion` that: +/// - Reads a `ShellContext` from the packed `CompletionContext`. +/// - Calls `CompletionHelper::exec_completion::<ThisProgram>(&ctx)` to generate suggestions. +/// - Routes the result to the completion renderer via `CompletionSuggest`. +/// 3. An internal renderer `__render_completion` that renders the suggestions via +/// `CompletionHelper::render_suggest`. +/// +/// When the `dispatch_tree` feature is enabled, it also imports the internal dispatcher +/// from the generated module into the parent scope for trie-based dispatch. +/// +/// This macro is called automatically by `gen_program!` and should not be called +/// directly by user code. #[cfg(feature = "comp")] #[proc_macro] pub fn program_comp_gen(input: TokenStream) -> TokenStream { @@ -1503,16 +1846,47 @@ pub fn register_type(input: TokenStream) -> TokenStream { func::gen_program::register_type_impl(input) } +/// Registers a chain mapping function into the global chain registry. +/// +/// This macro is called internally by `#[chain]` and is generally not needed +/// in user code. Each call stores a string entry containing the source-to-target +/// type mapping, which is later consumed by `gen_program!` to generate the +/// `has_chain` and `do_chain` dispatch logic in `ProgramCollect`. +/// +/// The entry string format is a match arm: the source variant maps to a call +/// that converts the value into a `ChainProcess` via the destination type. +/// +/// # Panics +/// +/// Panics if the global `CHAINS` mutex is poisoned. #[proc_macro] pub fn register_chain(input: TokenStream) -> TokenStream { func::gen_program::register_chain_impl(input) } +/// Registers a renderer mapping function into the global renderer registry. +/// +/// This macro is called internally by `#[renderer]` and is generally not +/// needed in user code. Each call stores a string entry containing the +/// type-to-render mapping, which is later consumed by `gen_program!` to +/// generate the `has_renderer` and `render` dispatch logic in `ProgramCollect`. +/// +/// The entry string format is a match arm: the type variant maps to a call +/// of the registered renderer function that produces a `RenderResult`. +/// +/// # Panics +/// +/// Panics if the global `RENDERERS` mutex is poisoned. #[proc_macro] pub fn register_renderer(input: TokenStream) -> TokenStream { func::gen_program::register_renderer_impl(input) } +/// Internal macro used by `gen_program!` to generate the fallback types for +/// error cases when no dispatcher or renderer is found. +/// +/// This macro is called automatically by `gen_program!` and should not +/// be called directly by user code. #[proc_macro] pub fn program_fallback_gen(input: TokenStream) -> TokenStream { func::gen_program::program_fallback_gen_impl(input) |
