From e735671acb3a81e1b7e334e56b9ef3963ba0c2fc Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Fri, 26 Jun 2026 06:08:12 +0800 Subject: 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`. --- mingling_core/src/any.rs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'mingling_core/src/any.rs') diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs index 8ee07f5..ef9f912 100644 --- a/mingling_core/src/any.rs +++ b/mingling_core/src/any.rs @@ -1,6 +1,3 @@ -#[cfg(feature = "general_renderer")] -use serde::Serialize; - use crate::Groupped; use crate::error::ChainProcessError; @@ -14,7 +11,8 @@ pub mod group; /// /// Note: /// - If an enum value that does not belong to this type is incorrectly specified, it will be **unsafely** unwrapped by the scheduler -/// - Under the `general_renderer` feature, the passed value must ensure it implements `serde::Serialize` +/// - Structured output via `--json`/`--yaml` is only available for types that implement +/// [`StructuralData`], which implies `serde::Serialize`. /// - It is recommended to use the `pack!` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to create types that can be converted to `AnyOutput`, which guarantees runtime safety #[derive(Debug)] pub struct AnyOutput { @@ -24,21 +22,7 @@ pub struct AnyOutput { } impl AnyOutput { - /// Create an `AnyOutput` from a `Send + Groupped + Serialize` type - #[cfg(feature = "general_renderer")] - pub fn new(value: T) -> Self - where - T: Send + Groupped + Serialize + 'static, - { - Self { - inner: Box::new(value), - type_id: std::any::TypeId::of::(), - member_id: T::member_id(), - } - } - /// Create an `AnyOutput` from a `Send + Groupped` type - #[cfg(not(feature = "general_renderer"))] pub fn new(value: T) -> Self where T: Send + Groupped + 'static, @@ -82,9 +66,14 @@ impl AnyOutput { ChainProcess::Ok((self, NextProcess::Renderer)) } - #[cfg(feature = "general_renderer")] - /// Restore `AnyOutput` back to the original Serialize type - pub fn restore(self) -> Option { + /// Restore `AnyOutput` back to the original concrete type. + /// + /// # Safety + /// + /// This is only safe when `T` matches the `TypeId` stored in the `AnyOutput`. + /// Generated code (via `gen_program!()`) guarantees this by dispatching on + /// `member_id` before calling `restore`. + pub fn restore(self) -> Option { if self.type_id == std::any::TypeId::of::() { match self.inner.downcast::() { Ok(boxed) => Some(*boxed), -- cgit