aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/lib.rs')
-rw-r--r--mingling_macros/src/lib.rs374
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)