From 57c53affe3542cb6bd4e79ee4c18f20a1bd76b2d Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 17 Aug 2026 05:49:19 +0800 Subject: refactor!: replace pack! macros with derive-based pipeline types Remove the `pack!`, `pack_err!`, `pack_structural!`, and `pack_err_structural!` macros, replacing all pipeline type definitions with `#[derive(Grouped)]` and `#[derive(Grouped, Wrap)]` attributes. This changes the generated struct shape from named-field structs with an `inner` field to tuple structs accessed via `.0`, and removes the auto-generated `name` and `info` fields from error types. --- mingling_macros/src/lib.rs | 238 +++++++++------------------------------------ 1 file changed, 46 insertions(+), 192 deletions(-) (limited to 'mingling_macros/src/lib.rs') diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 7da7db3..d0556dc 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -34,15 +34,13 @@ use attr::dispatcher_clap; use attr::program_setup; use attr::{chain, help, metadata, renderer}; use derive::{enum_tag, grouped, wrap}; +use func::dispatcher; #[cfg(feature = "extras")] use func::entry; #[cfg(feature = "extras")] pub(crate) use func::group as group_impl; -#[cfg(feature = "extras")] -use func::pack_err; #[cfg(feature = "comp")] use func::suggest; -use func::{dispatcher, pack}; use systems::res_injection; pub(crate) fn default_program_path() -> proc_macro2::TokenStream { quote::quote! { crate::ThisProgram } @@ -60,7 +58,7 @@ pub(crate) type Registry = OnceLock>>; pub(crate) static STRUCTURAL_RENDERERS: Registry = OnceLock::new(); /// Types explicitly marked with `#[derive(StructuralData)]` or created via -/// `pack_structural!` / `group_structural!`. +/// `group_structural!`. #[cfg(feature = "structural_renderer")] pub(crate) static STRUCTURED_TYPES: Registry = OnceLock::new(); @@ -183,163 +181,6 @@ pub fn group_structural(input: TokenStream) -> TokenStream { func::group_structural::group_structural(input) } -/// Creates a type-safe wrapper struct around an inner type, with automatic -/// trait implementations for use in the Mingling chain/render pipeline. -/// -/// The generated struct implements: `From`/`Into`, `AsRef`/`AsMut`, `Deref`/`DerefMut`, -/// `Default` (conditional on inner type), and conversion into `AnyOutput` / -/// `ChainProcess` for routing. -/// -/// # Syntax -/// -/// ```rust,ignore -/// // Default program name (uses `ThisProgram`): -/// pack!(TypeName = InnerType); -/// -/// // Explicit program name: -/// pack!(MyProgram, TypeName = InnerType); -/// ``` -/// -/// # Example -/// -/// ```rust,ignore -/// use mingling::macros::pack; -/// -/// // Creates `Hello` wrapping `String`, registered under `ThisProgram`: -/// pack!(Hello = String); -/// -/// // Creates `Greeting` wrapping `String`, registered under `MyApp`: -/// pack!(MyApp, Greeting = String); -/// ``` -/// -/// After expansion, `Hello` has: -/// - `Hello::new(String)` — constructor -/// - `Hello::to_chain()` — routes to the next chain processor -/// - `Hello::to_render()` — routes to a renderer -/// - `From for Hello`, `From for String` -/// - `Deref`, `DerefMut` -/// - `AsRef`, `AsMut` -/// - `Default` if `String: Default` -/// - `Into>`, `Into>` -/// - Implements `Grouped` with `member_id()` returning the enum variant -/// -/// The struct is also registered via `register_type!` so that `gen_program!` -/// can include it in the program enum. -/// -/// When the `structural_renderer` feature is enabled, the struct also gets -/// `#[derive(serde::Serialize)]`. -#[proc_macro] -pub fn pack(input: TokenStream) -> TokenStream { - pack::pack(input) -} - -/// Like `pack!` but also marks the type as supporting structured output -/// (JSON / YAML / TOML / RON) via `StructuralData`. -/// -/// # Syntax -/// -/// ```rust,ignore -/// pack_structural!(Info = (String, i32)); -/// ``` -/// -/// This is equivalent to: -/// ```rust,ignore -/// pack!(Info = (String, i32)); -/// impl ::mingling::StructuralData for Info {} -/// ``` -/// -/// Requires the `structural_renderer` feature. -#[cfg(feature = "structural_renderer")] -#[proc_macro] -pub fn pack_structural(input: TokenStream) -> TokenStream { - func::pack_structural::pack_structural(input) -} - -/// Creates an error struct with a `name: String` field and optional `info: Type` field. -/// -/// This macro provides a concise way to define error types that implement `Grouped` -/// and are registered for inclusion in the program enum. -/// -/// The `name` field is automatically set to the `snake_case` version of the struct name -/// at compile time. -/// -/// # Syntax -/// -/// Two forms are supported: -/// -/// ```rust,ignore -/// // Simple form — generates a struct with only `name: String` and a `Default` impl: -/// pack_err!(ErrorNotFound); -/// -/// // Typed form — generates a struct with `name: String` + `info: Type` and a `new(info)` constructor: -/// pack_err!(ErrorNotDir = PathBuf); -/// ``` -/// -/// # Generated code -/// -/// For `pack_err!(ErrorNotFound)`: -/// -/// ```rust,ignore -/// #[derive(::mingling::Grouped)] -/// pub struct ErrorNotFound { -/// name: String, -/// } -/// -/// impl Default for ErrorNotFound { -/// fn default() -> Self { -/// Self { -/// name: "error_not_found".into(), -/// } -/// } -/// } -/// ``` -/// -/// For `pack_err!(ErrorNotDir = PathBuf)`: -/// -/// ```rust,ignore -/// #[derive(::mingling::Grouped)] -/// pub struct ErrorNotDir { -/// name: String, -/// info: PathBuf, -/// } -/// -/// impl ErrorNotDir { -/// pub fn new(info: PathBuf) -> Self { -/// Self { -/// name: "error_not_dir".into(), -/// info, -/// } -/// } -/// } -/// ``` -/// -/// When the `structural_renderer` feature is enabled, the struct also gets -/// `#[derive(serde::Serialize)]`. -/// -/// 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) -} - -/// Like `pack_err!` but also marks the type for structured output -/// (JSON / YAML / TOML / RON) via `StructuralData`. -/// -/// # Syntax -/// -/// ```rust,ignore -/// pack_err_structural!(ErrorNotFound); -/// pack_err_structural!(ErrorNotDir = PathBuf); -/// ``` -/// -/// 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) -} - /// Early-returns the error from a `Result`, converting the `Ok` branch to the /// next chain process value. /// @@ -555,7 +396,7 @@ pub fn empty_result(input: TokenStream) -> TokenStream { /// /// The macro generates: /// -/// 1. **Entry struct** — A `pack!`-style wrapper around `Vec` (the raw args). +/// 1. **Entry struct** — A newtype wrapper around `Vec` (the raw args). /// Registered in the program enum via `register_type!`. /// 2. **Dispatcher struct** — A hidden zero-sized struct implementing [`Dispatcher`]: /// - `begin(args)` wraps `args` into the entry type and routes to chain. @@ -664,78 +505,89 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { /// # Sync Example /// /// ```rust,ignore -/// use mingling::macros::{chain, pack, gen_program}; +/// use mingling::macros::{chain, gen_program}; +/// use mingling::{Grouped, Wrap}; /// -/// pack!(MyOutput = String); +/// #[derive(Grouped, Wrap)] +/// pub struct MyOutput(String); /// /// #[chain] /// fn greet(prev: HelloEntry) -> Next { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); -/// MyOutput::new(name) +/// MyOutput(name) /// } /// ``` /// /// # Sync Example with Resource Injection /// /// ```rust,ignore -/// use mingling::macros::{chain, pack, gen_program}; +/// use mingling::macros::{chain, gen_program}; +/// use mingling::{Grouped, Wrap}; /// /// #[derive(Default, Clone)] /// struct UserName(String); /// -/// pack!(Greeting = String); -/// pack!(DisplayCount = ()); +/// #[derive(Grouped, Wrap)] +/// pub struct Greeting(String); +/// #[derive(Grouped, Wrap)] +/// pub struct DisplayCount(()); /// /// #[chain] /// fn greet(prev: HelloEntry, user_name: &UserName, count: &mut u64) -> Next { /// *count += 1; -/// Greeting::new(format!("Hello, {}!", user_name.0)) +/// Greeting(format!("Hello, {}!", user_name.0)) /// } /// ``` /// /// # Async Example (with `async` feature) /// /// ```rust,ignore -/// use mingling::macros::{chain, pack, gen_program}; +/// use mingling::macros::{chain, gen_program}; +/// use mingling::{Grouped, Wrap}; /// -/// pack!(MyOutput = String); +/// #[derive(Grouped, Wrap)] +/// pub struct MyOutput(String); /// /// #[chain] /// async fn greet(prev: HelloEntry) -> Next { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); /// some_async_fn(&name).await; -/// MyOutput::new(name) +/// MyOutput(name) /// } /// ``` /// /// # Async Example with Immutable Resource Injection /// /// ```rust,ignore -/// use mingling::macros::{chain, pack, gen_program}; +/// use mingling::macros::{chain, gen_program}; +/// use mingling::{Grouped, Wrap}; /// -/// pack!(MyOutput = String); +/// #[derive(Grouped, Wrap)] +/// pub struct MyOutput(String); /// /// #[chain] /// async fn greet(prev: HelloEntry, prefix: &Prefix) -> Next { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); /// some_async_fn(&name).await; -/// MyOutput::new(format!("{}{}", prefix.0, name)) +/// MyOutput(format!("{}{}", prefix.0, name)) /// } /// ``` /// /// # Async Example with Mutable Resource Injection /// /// ```rust,ignore -/// use mingling::macros::{chain, pack, gen_program}; +/// use mingling::macros::{chain, gen_program}; +/// use mingling::{Grouped, Wrap}; /// -/// pack!(MyOutput = String); +/// #[derive(Grouped, Wrap)] +/// pub struct MyOutput(String); /// /// #[chain] /// async fn greet(prev: HelloEntry, ec: &mut ResExitCode) -> Next { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); /// ec.exit_code = 42; /// some_async_fn(&name).await; -/// MyOutput::new(name) +/// MyOutput(name) /// } /// ``` /// @@ -777,10 +629,12 @@ pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream { /// # Example /// /// ```rust,ignore -/// use mingling::macros::{renderer, pack, gen_program}; +/// use mingling::macros::{renderer, gen_program}; +/// use mingling::{Grouped, Wrap}; /// use std::io::Write; /// -/// pack!(Greeting = String); +/// #[derive(Grouped, Wrap)] +/// pub struct Greeting(String); /// /// #[renderer] /// fn render_greeting(prev: Greeting) -> RenderResult { @@ -1063,7 +917,7 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { /// Creates a packed entry value from a list of string literals. /// /// This is a convenience macro for constructing entry wrapper types (created -/// via `pack!` or `dispatcher!`) with test data, typically used in unit tests +/// via `dispatcher!`) with test data, typically used in unit tests /// or quick prototypes. /// /// # Syntax @@ -1071,9 +925,9 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { /// Two forms: /// /// ```rust,ignore -/// // Named form — wraps into a specific pack type: +/// // Named form — wraps into a specific entry type: /// entry!(MyEntry, ["a", "b", "c"]) -/// // Expands to: MyEntry::new(vec!["a".to_string(), "b".to_string(), "c".to_string()]) +/// // Expands to: MyEntry(vec!["a".to_string(), "b".to_string(), "c".to_string()]) /// /// // Bracket form — returns Vec.into() for type inference: /// entry!["a", "b", "c"] @@ -1085,7 +939,7 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { /// ```rust,ignore /// use mingling::macros::entry; /// -/// // Named form (with a specific pack type): +/// // Named form (with a specific entry type): /// let args = entry!(MyEntry, ["--name", "Alice", "--count", "5"]); /// /// // Bracket form (type inference): @@ -1094,8 +948,7 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// # See also /// -/// - `pack!` — For creating the wrapper types used with `entry!`. -/// - `dispatcher!` — Which implicitly creates entry types via `pack!`. +/// - `dispatcher!` — Which implicitly creates entry types. #[cfg(feature = "extras")] #[proc_macro] pub fn entry(input: TokenStream) -> TokenStream { @@ -1204,11 +1057,12 @@ pub fn register_dispatcher(input: TokenStream) -> TokenStream { /// # Example /// /// ```rust,ignore -/// use mingling::macros::{help, pack, gen_program}; +/// use mingling::macros::{help, gen_program}; /// use mingling::{prelude::*, setup::BasicProgramSetup, RenderResult}; /// use std::io::Write; /// -/// pack!(MyEntry = Vec); +/// #[derive(Grouped, Wrap)] +/// pub struct MyEntry(Vec); /// /// #[help] /// fn help_my_entry(prev: MyEntry) -> RenderResult { @@ -1620,8 +1474,8 @@ pub fn derive_wrap(input: TokenStream) -> TokenStream { /// } /// ``` /// -/// This is equivalent to using `pack!` but works with custom structs that -/// have named fields. For simple wrappers, prefer `pack!`. +/// This is equivalent to using `#[derive(Grouped)]` but works with custom structs that +/// have named fields. #[proc_macro_derive(Grouped, attributes(group))] pub fn derive_grouped(input: TokenStream) -> TokenStream { grouped::derive_grouped(input) @@ -1813,7 +1667,7 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { /// Registers a type into the global packed types registry for inclusion in /// the program enum generated by `gen_program!`. /// -/// This macro is called internally by `pack!` and `#[derive(Grouped)]`(`macro.derive_grouped.html`) +/// This macro is called internally by `#[derive(Grouped)]` (`macro.derive_grouped.html`) /// and is generally not needed in user code. However, it can be used for manual /// registration if you are implementing custom type registration outside of /// the standard macros. @@ -1885,7 +1739,7 @@ pub fn program_fallback_gen(input: TokenStream) -> TokenStream { /// and its `ProgramCollect` implementation. /// /// This is the core code generation macro that: -/// 1. Collects all registered types (from `pack!`, `#[derive(Grouped)]`, etc.) and +/// 1. Collects all registered types (from `#[derive(Grouped)]`, etc.) and /// creates an enum with each type as a variant. /// 2. Generates the `Display` implementation for the enum. /// 3. Generates the `ProgramCollect` implementation that dispatches to all -- cgit