diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-04-05 20:33:57 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-04-05 20:33:57 +0800 |
| commit | b6876f9df6e3119331fac01c0bc954ca9f3c798b (patch) | |
| tree | 030301c8d434d51fa386e9a5fff77bce5899733b /mingling_core/src | |
| parent | b41e201e554c4ba5ad6f1e09ce58dd3d10b6a635 (diff) | |
Add general renderer support with serialization formats
Diffstat (limited to 'mingling_core/src')
| -rw-r--r-- | mingling_core/src/any.rs | 15 | ||||
| -rw-r--r-- | mingling_core/src/lib.rs | 8 | ||||
| -rw-r--r-- | mingling_core/src/markers.rs | 4 | ||||
| -rw-r--r-- | mingling_core/src/markers/group_process.rs | 2 | ||||
| -rw-r--r-- | mingling_core/src/markers/next_process.rs | 2 | ||||
| -rw-r--r-- | mingling_core/src/markers/this_program.rs | 2 | ||||
| -rw-r--r-- | mingling_core/src/program.rs | 15 | ||||
| -rw-r--r-- | mingling_core/src/program/config.rs | 62 | ||||
| -rw-r--r-- | mingling_core/src/program/exec.rs | 40 | ||||
| -rw-r--r-- | mingling_core/src/program/setup.rs | 5 | ||||
| -rw-r--r-- | mingling_core/src/program/setup/general_renderer.rs | 61 | ||||
| -rw-r--r-- | mingling_core/src/renderer.rs | 2 | ||||
| -rw-r--r-- | mingling_core/src/renderer/general.rs | 96 | ||||
| -rw-r--r-- | mingling_core/src/renderer/general/error.rs | 30 |
14 files changed, 328 insertions, 16 deletions
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs index d550ec7..57eddfb 100644 --- a/mingling_core/src/any.rs +++ b/mingling_core/src/any.rs @@ -23,7 +23,7 @@ pub struct AnyOutput<G> where G: Display, { - inner: Box<dyn std::any::Any + Send + 'static>, + pub(crate) inner: Box<dyn std::any::Any + Send + 'static>, pub type_id: std::any::TypeId, pub member_id: G, } @@ -81,6 +81,19 @@ where pub fn route_renderer(self) -> ChainProcess<G> { ChainProcess::Ok((self, Next::Renderer)) } + + #[cfg(feature = "general_renderer")] + /// Restore AnyOutput back to the original Serialize type + pub fn restore<T: Serialize + 'static>(self) -> Option<T> { + if self.type_id == std::any::TypeId::of::<T>() { + match self.inner.downcast::<T>() { + Ok(boxed) => Some(*boxed), + Err(_) => None, + } + } else { + None + } + } } impl<G> std::ops::Deref for AnyOutput<G> diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs index bcad91d..999c141 100644 --- a/mingling_core/src/lib.rs +++ b/mingling_core/src/lib.rs @@ -14,6 +14,9 @@ mod markers; mod program; mod renderer; +#[cfg(feature = "general_renderer")] +pub use crate::renderer::general::GeneralRenderer; + pub use crate::any::group::*; pub use crate::any::*; @@ -26,6 +29,8 @@ pub use crate::asset::renderer::*; pub mod error { pub use crate::asset::chain::error::*; pub use crate::exec::error::*; + #[cfg(feature = "general_renderer")] + pub use crate::renderer::general::error::*; } pub use crate::program::*; @@ -34,7 +39,8 @@ pub use crate::renderer::render_result::*; /// All marker types of `Mingling` that serve no practical purpose pub mod marker { - pub use crate::markers::group_process::*; + pub use crate::markers::next_process::*; + pub use crate::markers::this_program::*; } /// `Mingling`'s Program initialization system diff --git a/mingling_core/src/markers.rs b/mingling_core/src/markers.rs index 151a0d4..1c89ea8 100644 --- a/mingling_core/src/markers.rs +++ b/mingling_core/src/markers.rs @@ -1,2 +1,4 @@ #[doc(hidden)] -pub mod group_process; +pub mod next_process; +#[doc(hidden)] +pub mod this_program; diff --git a/mingling_core/src/markers/group_process.rs b/mingling_core/src/markers/group_process.rs deleted file mode 100644 index c9176f4..0000000 --- a/mingling_core/src/markers/group_process.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[allow(dead_code)] -pub struct GroupProcess; diff --git a/mingling_core/src/markers/next_process.rs b/mingling_core/src/markers/next_process.rs new file mode 100644 index 0000000..9169900 --- /dev/null +++ b/mingling_core/src/markers/next_process.rs @@ -0,0 +1,2 @@ +#[allow(dead_code)] +pub struct NextProcess; diff --git a/mingling_core/src/markers/this_program.rs b/mingling_core/src/markers/this_program.rs new file mode 100644 index 0000000..e8337f6 --- /dev/null +++ b/mingling_core/src/markers/this_program.rs @@ -0,0 +1,2 @@ +#[allow(dead_code)] +pub struct ThisProgram; diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs index 5d81234..0a24001 100644 --- a/mingling_core/src/program.rs +++ b/mingling_core/src/program.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "general_renderer")] +use crate::error::GeneralRendererSerializeError; use crate::{ AnyOutput, ChainProcess, RenderResult, asset::dispatcher::Dispatcher, error::ProgramExecuteError, @@ -31,6 +33,9 @@ where pub stdout_setting: ProgramStdoutSetting, pub user_context: ProgramUserContext, + + #[cfg(feature = "general_renderer")] + pub general_renderer_name: GeneralRendererSetting, } impl<C, G> Program<C, G> @@ -47,6 +52,9 @@ where dispatcher: Vec::new(), stdout_setting: Default::default(), user_context: Default::default(), + + #[cfg(feature = "general_renderer")] + general_renderer_name: GeneralRendererSetting::Disable, } } @@ -115,6 +123,13 @@ pub trait ProgramCollect { /// Whether the program has a chain that can handle the current [AnyOutput](./struct.AnyOutput.html) fn has_chain(any: &AnyOutput<Self::Enum>) -> bool; + + /// Perform general rendering and presentation of any type + #[cfg(feature = "general_renderer")] + fn general_render( + any: AnyOutput<Self::Enum>, + setting: &GeneralRendererSetting, + ) -> Result<RenderResult, GeneralRendererSerializeError>; } #[macro_export] diff --git a/mingling_core/src/program/config.rs b/mingling_core/src/program/config.rs index 6ad0a38..2f5de4c 100644 --- a/mingling_core/src/program/config.rs +++ b/mingling_core/src/program/config.rs @@ -17,7 +17,7 @@ impl Default for ProgramStdoutSetting { } } -/// Program stdout settings +/// Program user context #[derive(Debug, Clone, Default)] pub struct ProgramUserContext { /// View help information instead of running the command @@ -26,3 +26,63 @@ pub struct ProgramUserContext { /// Skip user confirmation step pub confirm: bool, } + +#[cfg(feature = "general_renderer")] +#[derive(Debug, Clone, Default)] +pub enum GeneralRendererSetting { + #[default] + Disable, + Json, + JsonPretty, + Yaml, + Toml, + Ron, + RonPretty, +} + +#[cfg(feature = "general_renderer")] +impl std::str::FromStr for GeneralRendererSetting { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match just_fmt::kebab_case!(s).as_str() { + "disable" => Ok(GeneralRendererSetting::Disable), + "json" => Ok(GeneralRendererSetting::Json), + "json-pretty" => Ok(GeneralRendererSetting::JsonPretty), + "yaml" => Ok(GeneralRendererSetting::Yaml), + "toml" => Ok(GeneralRendererSetting::Toml), + "ron" => Ok(GeneralRendererSetting::Ron), + "ron-pretty" => Ok(GeneralRendererSetting::RonPretty), + _ => Err(format!("Invalid renderer: '{}'", s)), + } + } +} + +#[cfg(feature = "general_renderer")] +impl From<&str> for GeneralRendererSetting { + fn from(s: &str) -> Self { + s.parse().unwrap_or(GeneralRendererSetting::Disable) + } +} + +#[cfg(feature = "general_renderer")] +impl From<String> for GeneralRendererSetting { + fn from(s: String) -> Self { + s.as_str().into() + } +} + +#[cfg(feature = "general_renderer")] +impl std::fmt::Display for GeneralRendererSetting { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GeneralRendererSetting::Disable => write!(f, "disable"), + GeneralRendererSetting::Json => write!(f, "json"), + GeneralRendererSetting::JsonPretty => write!(f, "json-pretty"), + GeneralRendererSetting::Yaml => write!(f, "yaml"), + GeneralRendererSetting::Toml => write!(f, "toml"), + GeneralRendererSetting::Ron => write!(f, "ron"), + GeneralRendererSetting::RonPretty => write!(f, "ron-pretty"), + } + } +} diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs index 29cfa1d..1a4f6ff 100644 --- a/mingling_core/src/program/exec.rs +++ b/mingling_core/src/program/exec.rs @@ -23,14 +23,16 @@ where Ok((dispatcher, args)) => { // Entry point current = match dispatcher.begin(args) { - ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C, G>(any)), + ChainProcess::Ok((any, Next::Renderer)) => { + return Ok(render::<C, G>(&program, any)); + } ChainProcess::Ok((any, Next::Chain)) => any, ChainProcess::Err(e) => return Err(e.into()), }; } Err(ProgramInternalExecuteError::DispatcherNotFound) => { // No matching Dispatcher is found - current = C::build_dispatcher_not_found(program.args); + current = C::build_dispatcher_not_found(program.args.clone()); } Err(e) => return Err(e), }; @@ -42,16 +44,16 @@ where // If a chain exists, execute as a chain if C::has_chain(¤t) { match C::do_chain(current).await { - ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C, G>(any)), + ChainProcess::Ok((any, Next::Renderer)) => { + return Ok(render::<C, G>(&program, any)); + } ChainProcess::Ok((any, Next::Chain)) => any, ChainProcess::Err(e) => return Err(e.into()), } } // If no chain exists, attempt to render else if C::has_renderer(¤t) { - let mut render_result = RenderResult::default(); - C::render(current, &mut render_result); - return Ok(render_result); + return Ok(render::<C, G>(&program, current)); } // No renderer exists else { @@ -113,10 +115,28 @@ where } #[inline(always)] -fn render<C: ProgramCollect<Enum = G>, G: Display>(any: AnyOutput<G>) -> RenderResult { - let mut render_result = RenderResult::default(); - C::render(any, &mut render_result); - render_result +#[allow(unused_variables)] +fn render<C: ProgramCollect<Enum = G>, G: Display>( + program: &Program<C, G>, + any: AnyOutput<G>, +) -> RenderResult { + #[cfg(not(feature = "general_renderer"))] + { + let mut render_result = RenderResult::default(); + C::render(any, &mut render_result); + render_result + } + #[cfg(feature = "general_renderer")] + { + match program.general_renderer_name { + super::GeneralRendererSetting::Disable => { + let mut render_result = RenderResult::default(); + C::render(any, &mut render_result); + render_result + } + _ => C::general_render(any, &program.general_renderer_name).unwrap(), + } + } } // Get all registered dispatcher names from the program diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs index 1f16f80..7b534f3 100644 --- a/mingling_core/src/program/setup.rs +++ b/mingling_core/src/program/setup.rs @@ -5,6 +5,11 @@ use crate::{ProgramCollect, program::Program}; mod basic; pub use basic::*; +#[cfg(feature = "general_renderer")] +mod general_renderer; +#[cfg(feature = "general_renderer")] +pub use general_renderer::*; + pub trait ProgramSetup<C, G> where C: ProgramCollect, diff --git a/mingling_core/src/program/setup/general_renderer.rs b/mingling_core/src/program/setup/general_renderer.rs new file mode 100644 index 0000000..8b41b68 --- /dev/null +++ b/mingling_core/src/program/setup/general_renderer.rs @@ -0,0 +1,61 @@ +use std::fmt::Display; + +use crate::{ + ProgramCollect, + program::{Program, setup::ProgramSetup}, +}; + +/// Sets up the general renderer for the program: +/// +/// - Adds a `--renderer` global argument to specify the renderer type +pub struct GeneralRendererSimpleSetup; + +impl<C, G> ProgramSetup<C, G> for GeneralRendererSimpleSetup +where + C: ProgramCollect, + G: Display, +{ + fn setup(&mut self, program: &mut Program<C, G>) { + program.global_argument("--renderer", |p, renderer| { + p.general_renderer_name = renderer.into(); + }); + } +} + +/// Sets up the general renderer for the program: +/// +/// - Adds global flags to specify the renderer type: +/// * `--json` for JSON output +/// * `--json-pretty` for pretty-printed JSON output +/// * `--yaml` for YAML output +/// * `--toml` for TOML output +/// * `--ron` for RON output +/// * `--ron-pretty` for pretty-printed RON output +pub struct GeneralRendererSetup; + +impl<C, G> ProgramSetup<C, G> for GeneralRendererSetup +where + C: ProgramCollect, + G: Display, +{ + fn setup(&mut self, program: &mut Program<C, G>) { + program.global_flag("--json", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::Json + }); + program.global_flag("--json-pretty", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::JsonPretty.into(); + }); + program.global_flag("--yaml", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::Yaml.into(); + }); + program.global_flag("--toml", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::Toml.into(); + }); + program.global_flag("--ron", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::Ron.into(); + }); + program.global_flag("--ron-pretty", |p| { + p.general_renderer_name = crate::GeneralRendererSetting::RonPretty.into(); + }); + } +} diff --git a/mingling_core/src/renderer.rs b/mingling_core/src/renderer.rs index 631092b..33dd08d 100644 --- a/mingling_core/src/renderer.rs +++ b/mingling_core/src/renderer.rs @@ -1 +1,3 @@ +#[cfg(feature = "general_renderer")] +pub mod general; pub mod render_result; diff --git a/mingling_core/src/renderer/general.rs b/mingling_core/src/renderer/general.rs new file mode 100644 index 0000000..83ea911 --- /dev/null +++ b/mingling_core/src/renderer/general.rs @@ -0,0 +1,96 @@ +use crate::{ + GeneralRendererSetting, RenderResult, renderer::general::error::GeneralRendererSerializeError, +}; +use serde::Serialize; + +pub mod error; +pub struct GeneralRenderer; + +impl GeneralRenderer { + // Renders data in the specified format to the given RenderResult. + pub fn render<T: Serialize + Send>( + data: &T, + setting: &GeneralRendererSetting, + r: &mut RenderResult, + ) -> Result<(), GeneralRendererSerializeError> { + match setting { + GeneralRendererSetting::Disable => Ok(()), + GeneralRendererSetting::Json => Self::render_to_json(data, r), + GeneralRendererSetting::JsonPretty => Self::render_to_json_pretty(data, r), + GeneralRendererSetting::Yaml => Self::render_to_yaml(data, r), + GeneralRendererSetting::Toml => Self::render_to_toml(data, r), + GeneralRendererSetting::Ron => Self::render_to_ron(data, r), + GeneralRendererSetting::RonPretty => Self::render_to_ron_pretty(data, r), + } + } + + /// Serializes data to JSON format and writes it to the render result. + pub 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(format!("{}", json_string).as_str()); + Ok(()) + } + + /// Serializes data to pretty-printed JSON format and writes it to the render result. + pub 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(format!("{}", json_string).as_str()); + Ok(()) + } + + /// Serializes data to RON format and writes it to the render result. + pub 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(format!("{}", ron_string).as_str()); + Ok(()) + } + + /// Serializes data to pretty-printed RON format and writes it to the render result. + pub 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 ron_string = ron::ser::to_string_pretty(data, pretty_config) + .map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?; + r.print(format!("{}", ron_string).as_str()); + Ok(()) + } + + /// Serializes data to TOML format and writes it to the render result. + pub 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(format!("{}", toml_string).as_str()); + Ok(()) + } + + /// Serializes data to YAML format and writes it to the render result. + pub 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(format!("{}", yaml_string).as_str()); + Ok(()) + } +} diff --git a/mingling_core/src/renderer/general/error.rs b/mingling_core/src/renderer/general/error.rs new file mode 100644 index 0000000..885b5bd --- /dev/null +++ b/mingling_core/src/renderer/general/error.rs @@ -0,0 +1,30 @@ +#[derive(Debug)] +pub struct GeneralRendererSerializeError { + error: String, +} + +impl GeneralRendererSerializeError { + pub fn new(error: String) -> Self { + Self { error } + } +} + +impl From<&str> for GeneralRendererSerializeError { + fn from(s: &str) -> Self { + Self::new(s.to_string()) + } +} + +impl std::ops::Deref for GeneralRendererSerializeError { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.error + } +} + +impl Into<String> for GeneralRendererSerializeError { + fn into(self) -> String { + self.error + } +} |
