summaryrefslogtreecommitdiff
path: root/mingling_core/src
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_core/src')
-rw-r--r--mingling_core/src/any.rs15
-rw-r--r--mingling_core/src/lib.rs8
-rw-r--r--mingling_core/src/markers.rs4
-rw-r--r--mingling_core/src/markers/group_process.rs2
-rw-r--r--mingling_core/src/markers/next_process.rs2
-rw-r--r--mingling_core/src/markers/this_program.rs2
-rw-r--r--mingling_core/src/program.rs15
-rw-r--r--mingling_core/src/program/config.rs62
-rw-r--r--mingling_core/src/program/exec.rs40
-rw-r--r--mingling_core/src/program/setup.rs5
-rw-r--r--mingling_core/src/program/setup/general_renderer.rs61
-rw-r--r--mingling_core/src/renderer.rs2
-rw-r--r--mingling_core/src/renderer/general.rs96
-rw-r--r--mingling_core/src/renderer/general/error.rs30
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(&current) {
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(&current) {
- 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
+ }
+}