aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/renderer
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_core/src/renderer
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_core/src/renderer')
-rw-r--r--mingling_core/src/renderer/general.rs58
-rw-r--r--mingling_core/src/renderer/general/structural_data.rs15
2 files changed, 50 insertions, 23 deletions
diff --git a/mingling_core/src/renderer/general.rs b/mingling_core/src/renderer/general.rs
index 1a9647b..e6da06b 100644
--- a/mingling_core/src/renderer/general.rs
+++ b/mingling_core/src/renderer/general.rs
@@ -4,13 +4,16 @@ use crate::{
use serde::Serialize;
pub mod error;
+pub mod structural_data;
+
+use structural_data::StructuralData;
/// A general renderer that supports multiple serialization formats.
///
/// The `GeneralRenderer` provides methods to serialize data into various formats
/// including JSON, YAML, TOML, and RON, with support for both regular and
/// pretty-printed variants. It is designed to work with types that implement
-/// the `Serialize` trait.
+/// the [`StructuralData`] trait (which implies `Serialize`).
pub struct GeneralRenderer;
impl GeneralRenderer {
@@ -20,7 +23,7 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[allow(unused_variables)]
- pub fn render<T: Serialize + Send>(
+ pub fn render<T: StructuralData + Send>(
data: &T,
setting: &GeneralRendererSetting,
r: &mut RenderResult,
@@ -48,13 +51,13 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "json_serde_fmt")]
- pub fn render_to_json<T: Serialize + Send>(
+ fn render_to_json<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
let json_string = serde_json::to_string(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(json_string.clone().as_str());
+ r.print(&json_string);
Ok(())
}
@@ -64,13 +67,13 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "json_serde_fmt")]
- pub fn render_to_json_pretty<T: Serialize + Send>(
+ fn render_to_json_pretty<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
let json_string = serde_json::to_string_pretty(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(json_string.clone().as_str());
+ r.print(&json_string);
Ok(())
}
@@ -80,13 +83,13 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "ron_serde_fmt")]
- pub fn render_to_ron<T: Serialize + Send>(
+ fn render_to_ron<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
let ron_string = ron::ser::to_string(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(ron_string.to_string().as_str());
+ r.print(&ron_string);
Ok(())
}
@@ -96,17 +99,17 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "ron_serde_fmt")]
- pub fn render_to_ron_pretty<T: Serialize + Send>(
+ fn render_to_ron_pretty<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
- let mut pretty_config = ron::ser::PrettyConfig::new();
- pretty_config.new_line = std::borrow::Cow::from("\n");
- pretty_config.indentor = std::borrow::Cow::from(" ");
+ let pretty_config = ron::ser::PrettyConfig::new()
+ .new_line("\n")
+ .indentor(" ");
let ron_string = ron::ser::to_string_pretty(data, pretty_config)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(ron_string.to_string().as_str());
+ r.print(&ron_string);
Ok(())
}
@@ -116,13 +119,13 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "toml_serde_fmt")]
- pub fn render_to_toml<T: Serialize + Send>(
+ fn render_to_toml<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
let toml_string =
toml::to_string(data).map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(toml_string.to_string().as_str());
+ r.print(&toml_string);
Ok(())
}
@@ -132,13 +135,13 @@ impl GeneralRenderer {
///
/// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "yaml_serde_fmt")]
- pub fn render_to_yaml<T: Serialize + Send>(
+ fn render_to_yaml<T: Serialize + Send>(
data: &T,
r: &mut RenderResult,
) -> Result<(), GeneralRendererSerializeError> {
let yaml_string = serde_yaml::to_string(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(yaml_string.to_string().as_str());
+ r.print(&yaml_string);
Ok(())
}
}
@@ -155,6 +158,9 @@ mod tests {
value: i32,
}
+ impl crate::__private::StructuralDataSealed for TestData {}
+ impl StructuralData for TestData {}
+
fn test_data() -> TestData {
TestData {
name: "hello".into(),
@@ -175,7 +181,8 @@ mod tests {
#[test]
fn test_render_to_json() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_json(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::Json, &mut r);
assert!(result.is_ok());
assert!(!r.is_empty());
let output: String = r.into();
@@ -189,7 +196,8 @@ mod tests {
#[test]
fn test_render_to_json_pretty() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_json_pretty(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::JsonPretty, &mut r);
assert!(result.is_ok());
let output: String = r.into();
// Pretty JSON has newlines
@@ -200,7 +208,8 @@ mod tests {
#[test]
fn test_render_to_yaml() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_yaml(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::Yaml, &mut r);
assert!(result.is_ok());
assert!(!r.is_empty());
}
@@ -209,7 +218,8 @@ mod tests {
#[test]
fn test_render_to_toml() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_toml(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::Toml, &mut r);
assert!(result.is_ok());
assert!(!r.is_empty());
}
@@ -218,7 +228,8 @@ mod tests {
#[test]
fn test_render_to_ron() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_ron(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::Ron, &mut r);
assert!(result.is_ok());
assert!(!r.is_empty());
}
@@ -227,7 +238,8 @@ mod tests {
#[test]
fn test_render_to_ron_pretty() {
let mut r = RenderResult::default();
- let result = GeneralRenderer::render_to_ron_pretty(&test_data(), &mut r);
+ let result =
+ GeneralRenderer::render(&test_data(), &GeneralRendererSetting::RonPretty, &mut r);
assert!(result.is_ok());
let output: String = r.into();
assert!(output.contains('\n'));
diff --git a/mingling_core/src/renderer/general/structural_data.rs b/mingling_core/src/renderer/general/structural_data.rs
new file mode 100644
index 0000000..ac6363e
--- /dev/null
+++ b/mingling_core/src/renderer/general/structural_data.rs
@@ -0,0 +1,15 @@
+use serde::Serialize;
+
+/// Marker trait for types that support structured output (JSON / YAML / TOML / RON).
+///
+/// This trait is a **supertrait** of `serde::Serialize` and is sealed via
+/// `__private::StructuralDataSealed`. It can only be implemented through:
+///
+/// - `#[derive(StructuralData)]`
+/// - `pack_structural!`
+/// - `group_structural!`
+///
+/// These entry points also register the type in the global `STRUCTURED_TYPES`
+/// registry, which is required for the `general_render` match arm to be generated.
+#[doc(hidden)]
+pub trait StructuralData: Serialize + crate::__private::StructuralDataSealed {}