aboutsummaryrefslogtreecommitdiff
path: root/mingling
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-26 06:08:12 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-26 06:08:12 +0800
commite735671acb3a81e1b7e334e56b9ef3963ba0c2fc (patch)
tree46562d6630bb1582b41b6741a7a4f482febf84da /mingling
parent473cd8e575d03d8bd5439e81cb6835f56a1e964f (diff)
feat(core): decouple structured output from Groupped trait
Introduce `StructuralData` sealed trait and `pack_structural!` / `group_structural!` / `derive(StructuralData)` macros to control structured rendering separately from grouping. `Groupped` no longer requires `Serialize`.
Diffstat (limited to 'mingling')
-rw-r--r--mingling/Cargo.toml8
-rw-r--r--mingling/src/example_docs.rs66
-rw-r--r--mingling/src/lib.rs19
3 files changed, 76 insertions, 17 deletions
diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml
index cce64b5..427bed6 100644
--- a/mingling/Cargo.toml
+++ b/mingling/Cargo.toml
@@ -59,10 +59,10 @@ general_renderer_empty = [
]
general_renderer_full = [
- "mingling_core/general_renderer",
- "dep:serde",
- "mingling_macros/general_renderer",
- "all_serde_fmt",
+ "general_renderer",
+ "yaml_serde_fmt",
+ "toml_serde_fmt",
+ "ron_serde_fmt",
]
all_serde_fmt = [
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
index ea8e539..266a449 100644
--- a/mingling/src/example_docs.rs
+++ b/mingling/src/example_docs.rs
@@ -1212,7 +1212,7 @@ pub mod example_exitcode {}
/// Source code (./src/main.rs)
/// ```ignore
/// use mingling::prelude::*;
-/// use mingling::{parser::Picker, setup::GeneralRendererSetup, Groupped};
+/// use mingling::{parser::Picker, setup::GeneralRendererSetup, StructuralData, Groupped};
/// use serde::Serialize;
///
/// dispatcher!("render", CMDRender => EntryRender);
@@ -1228,11 +1228,12 @@ pub mod example_exitcode {}
/// // --------- IMPORTANT ---------
/// // For beautiful output structure, do not use `pack!` to wrap the types that need to be output.
/// // Instead, manually implement
-/// // ____________________ Implement serde::Serialize
-/// // / _________ Implement mingling::Groupped
-/// // | / to ensure Mingling can recognize the type
-/// // vvvvvvvvv vvvvvvvv
-/// #[derive(Serialize, Groupped)]
+/// // __________________________________ Mark as structured data so it can be rendered
+/// // / ____________________ Implement serde::Serialize
+/// // | / _________ Implement mingling::Groupped
+/// // | | / to ensure Mingling can recognize the type
+/// // vvvvvvvvvvvv vvvvvvvvv vvvvvvvv
+/// #[derive(StructuralData, Serialize, Groupped)]
/// struct Info {
/// #[serde(rename = "member_name")]
/// name: String,
@@ -1686,21 +1687,23 @@ pub mod example_outside_type {}
/// Run:
/// ```bash
/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find
-/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find --json
/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find Cargo.toml
-/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find Cargo.toml --json
/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find src
-/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find src --json
+/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find-structural --json
+/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find-structural Cargo.toml --json
+/// cargo run --manifest-path examples/example-pack-err/Cargo.toml --quiet -- find-structural src --json
/// ```
///
/// Output:
/// ```plaintext
/// Search path not provided
-/// {"name":"error_not_found"}
/// Not a directory: Cargo.toml
-/// {"name":"error_not_dir","info":"Cargo.toml"}
/// Found directory: src
+/// {"name":"error_not_found"}
+/// {"name":"error_not_dir","info":"Cargo.toml"}
/// {"inner":"src"}
+/// {"name":"error_not_found_structural"}
+/// {"name":"error_not_dir_structural","info":"Cargo.toml"}
/// ```
///
/// Source code (./Cargo.toml)
@@ -1730,6 +1733,7 @@ pub mod example_outside_type {}
/// use std::path::PathBuf;
///
/// dispatcher!("find", CMDFind => EntryFind);
+/// dispatcher!("find-structural", CMDFindStructural => EntryFindStructural);
///
/// // --------- IMPORTANT ---------
/// // `pack_err!` is a convenient macro for defining error types.
@@ -1753,8 +1757,14 @@ pub mod example_outside_type {}
/// // Typed form — name = "error_not_dir"
/// pack_err!(ErrorNotDir = PathBuf);
///
-/// // Success type using traditional pack!
-/// pack!(ResultPath = PathBuf);
+/// // Simple form — with StructuralData support for --json / --yaml
+/// pack_err_structural!(ErrorNotFoundStructural);
+///
+/// // Typed form — with StructuralData support for --json / --yaml
+/// pack_err_structural!(ErrorNotDirStructural = PathBuf);
+///
+/// // Success type with StructuralData support
+/// pack_structural!(ResultPath = PathBuf);
///
/// #[chain]
/// fn handle_find(args: EntryFind) -> Next {
@@ -1773,6 +1783,23 @@ pub mod example_outside_type {}
/// }
/// }
///
+/// #[chain]
+/// fn handle_find_structural(args: EntryFindStructural) -> Next {
+/// let Some(path_str) = args.inner.first().cloned() else {
+/// // No path provided → use the simple error form (Default)
+/// return ErrorNotFoundStructural::default().to_render();
+/// };
+///
+/// let path = PathBuf::from(&path_str);
+/// if path.is_dir() {
+/// // Is a directory → success
+/// ResultPath::new(path).to_render()
+/// } else {
+/// // Not a directory (or doesn't exist) → use the typed error form
+/// ErrorNotDirStructural::new(path).to_render()
+/// }
+/// }
+///
/// /// Renders the successful result with the found directory path.
/// #[renderer]
/// fn render_result_path(path: ResultPath) {
@@ -1791,6 +1818,18 @@ pub mod example_outside_type {}
/// r_println!("Not a directory: {}", err.info.display());
/// }
///
+/// /// Renders the structural error when no search path is provided.
+/// #[renderer]
+/// fn render_error_not_found_structural(_: ErrorNotFoundStructural) {
+/// r_println!("Search path not provided");
+/// }
+///
+/// /// Renders the structural error when the given path is not a directory.
+/// #[renderer]
+/// fn render_error_not_dir_structural(err: ErrorNotDirStructural) {
+/// r_println!("Not a directory: {}", err.info.display());
+/// }
+///
/// gen_program!();
///
/// fn main() {
@@ -1798,6 +1837,7 @@ pub mod example_outside_type {}
/// // Add GeneralRendererSetup to support --json / --yaml flags
/// program.with_setup(GeneralRendererSetup);
/// program.with_dispatcher(CMDFind);
+/// program.with_dispatcher(CMDFindStructural);
/// let _ = program.exec();
/// }
/// ```
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 70b69bc..4c49f15 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -96,15 +96,24 @@ pub mod macros {
/// Used to register an external type as a group member
#[cfg(feature = "extra_macros")]
pub use mingling_macros::group;
+ /// Like `group!` but also marks the type for structured output
+ #[cfg(all(feature = "general_renderer", feature = "extra_macros"))]
+ pub use mingling_macros::group_structural;
/// Used to generate a struct implementing the `HelpRequest` trait via a method
pub use mingling_macros::help;
/// Used to create a `Node` struct via a literal
pub use mingling_macros::node;
/// Used to create a wrapper type for use with `Chain` and `Renderer`
pub use mingling_macros::pack;
+ /// Like `pack!` but also marks the type for structured output
+ #[cfg(feature = "general_renderer")]
+ pub use mingling_macros::pack_structural;
/// Used to create an error struct with automatic `name` field
#[cfg(feature = "extra_macros")]
pub use mingling_macros::pack_err;
+ /// Like `pack_err!` but also marks the type for structured output
+ #[cfg(all(feature = "general_renderer", feature = "extra_macros"))]
+ pub use mingling_macros::pack_err_structural;
#[cfg(feature = "comp")]
#[doc(hidden)]
pub use mingling_macros::program_comp_gen;
@@ -148,6 +157,10 @@ pub use mingling_macros::EnumTag;
/// derive macro Groupped
pub use mingling_macros::Groupped;
+/// derive macro `StructuralData` — marks a type as supporting structured output
+#[cfg(feature = "general_renderer")]
+pub use mingling_macros::StructuralData;
+
/// Example projects for `Mingling`, for learning how to use `Mingling`
pub mod _mingling_examples {
pub use crate::example_docs::*;
@@ -200,9 +213,15 @@ pub mod prelude {
pub use crate::macros::gen_program;
/// Re-export of the `pack` macro for creating wrapper types.
pub use crate::macros::pack;
+ /// Like `pack!` but also marks the type for structured output
+ #[cfg(feature = "general_renderer")]
+ pub use mingling_macros::pack_structural;
/// Re-export of the `pack_err` macro for creating error types.
#[cfg(feature = "extra_macros")]
pub use crate::macros::pack_err;
+ /// Like `pack_err!` but also marks the type for structured output
+ #[cfg(all(feature = "general_renderer", feature = "extra_macros"))]
+ pub use mingling_macros::pack_err_structural;
/// Re-export of the `r_print` macro for printing within a renderer context.
pub use crate::macros::r_print;
/// Re-export of the `r_println` macro for printing with a newline within a renderer