From b6876f9df6e3119331fac01c0bc954ca9f3c798b Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sun, 5 Apr 2026 20:33:57 +0800 Subject: Add general renderer support with serialization formats --- mingling/Cargo.lock | 173 +++++++++++++++++- mingling/Cargo.toml | 2 +- mingling/src/input.rs | 2 + mingling/src/input/input_field.rs | 1 + mingling/src/lib.rs | 2 + mingling_core/Cargo.lock | 177 ++++++++++++++++++ mingling_core/Cargo.toml | 13 +- mingling_core/src/any.rs | 15 +- mingling_core/src/lib.rs | 8 +- mingling_core/src/markers.rs | 4 +- mingling_core/src/markers/group_process.rs | 2 - mingling_core/src/markers/next_process.rs | 2 + mingling_core/src/markers/this_program.rs | 2 + mingling_core/src/program.rs | 15 ++ mingling_core/src/program/config.rs | 62 ++++++- mingling_core/src/program/exec.rs | 40 +++-- mingling_core/src/program/setup.rs | 5 + .../src/program/setup/general_renderer.rs | 61 +++++++ mingling_core/src/renderer.rs | 2 + mingling_core/src/renderer/general.rs | 96 ++++++++++ mingling_core/src/renderer/general/error.rs | 30 ++++ mingling_macros/Cargo.lock | 2 +- mingling_macros/Cargo.toml | 4 +- mingling_macros/src/chain.rs | 10 +- mingling_macros/src/lib.rs | 35 ++++ mingling_macros/src/pack.rs | 4 +- mingling_macros/src/program_setup.rs | 199 +++++++++++++++++++++ mingling_macros/src/renderer.rs | 21 +++ 28 files changed, 960 insertions(+), 29 deletions(-) create mode 100644 mingling/src/input.rs create mode 100644 mingling/src/input/input_field.rs delete mode 100644 mingling_core/src/markers/group_process.rs create mode 100644 mingling_core/src/markers/next_process.rs create mode 100644 mingling_core/src/markers/this_program.rs create mode 100644 mingling_core/src/program/setup/general_renderer.rs create mode 100644 mingling_core/src/renderer/general.rs create mode 100644 mingling_core/src/renderer/general/error.rs create mode 100644 mingling_macros/src/program_setup.rs diff --git a/mingling/Cargo.lock b/mingling/Cargo.lock index ef9be06..edb7731 100644 --- a/mingling/Cargo.lock +++ b/mingling/Cargo.lock @@ -2,18 +2,61 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "just_fmt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e" +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "mingling" version = "0.1.4" @@ -30,14 +73,18 @@ name = "mingling_core" version = "0.1.3" dependencies = [ "just_fmt", + "ron", "serde", + "serde_json", + "serde_yaml", "thiserror", "tokio", + "toml", ] [[package]] name = "mingling_macros" -version = "0.1.2" +version = "0.1.3" dependencies = [ "just_fmt", "once_cell", @@ -76,6 +123,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" +dependencies = [ + "bitflags", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "serde" version = "1.0.228" @@ -106,6 +173,41 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "size" version = "0.5.0" @@ -153,8 +255,77 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.1", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml index 68f37f9..a915fea 100644 --- a/mingling/Cargo.toml +++ b/mingling/Cargo.toml @@ -19,7 +19,7 @@ full = ["mingling_core/full", "general_renderer", "parser"] general_renderer = [ "mingling_core/general_renderer", "dep:serde", - "mingling_macros/serde", + "mingling_macros/general_renderer", ] parser = ["dep:size"] diff --git a/mingling/src/input.rs b/mingling/src/input.rs new file mode 100644 index 0000000..0f6b59f --- /dev/null +++ b/mingling/src/input.rs @@ -0,0 +1,2 @@ +pub mod input_field; +pub use crate::input::input_field::*; diff --git a/mingling/src/input/input_field.rs b/mingling/src/input/input_field.rs new file mode 100644 index 0000000..71eb7ed --- /dev/null +++ b/mingling/src/input/input_field.rs @@ -0,0 +1 @@ +pub struct InputField {} diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 2b40989..1d54140 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -73,6 +73,8 @@ pub mod macros { pub use mingling_macros::node; /// Used to create a wrapper type for use with `Chain` and `Renderer` pub use mingling_macros::pack; + // Used to generate program setup + pub use mingling_macros::program_setup; /// Used to print content within a `Renderer` context pub use mingling_macros::r_print; /// Used to print content with a newline within a `Renderer` context diff --git a/mingling_core/Cargo.lock b/mingling_core/Cargo.lock index a938576..60890b4 100644 --- a/mingling_core/Cargo.lock +++ b/mingling_core/Cargo.lock @@ -2,28 +2,81 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "just_fmt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e" +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "mingling_core" version = "0.1.3" dependencies = [ "just_fmt", + "ron", "serde", + "serde_json", + "serde_yaml", "thiserror", "tokio", + "toml", ] +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -48,6 +101,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" +dependencies = [ + "bitflags", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "serde" version = "1.0.228" @@ -78,6 +151,41 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "syn" version = "2.0.117" @@ -119,8 +227,77 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.1", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/mingling_core/Cargo.toml b/mingling_core/Cargo.toml index 4660f78..6f97016 100644 --- a/mingling_core/Cargo.toml +++ b/mingling_core/Cargo.toml @@ -9,10 +9,21 @@ repository = "https://github.com/catilgrass/mingling" [features] default = [] full = ["general_renderer"] -general_renderer = ["dep:serde"] +general_renderer = [ + "dep:serde", + "dep:ron", + "dep:serde_json", + "dep:serde_yaml", + "dep:toml", +] [dependencies] just_fmt = "0.1.2" serde = { version = "1.0", features = ["derive"], optional = true } thiserror = "2" tokio = { version = "1", features = ["io-std", "io-util"] } + +ron = { version = "0.12.1", optional = true } +serde_json = { version = "1", optional = true } +serde_yaml = { version = "0.9", optional = true } +toml = { version = "0.9.8", optional = true } 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 where G: Display, { - inner: Box, + pub(crate) inner: Box, pub type_id: std::any::TypeId, pub member_id: G, } @@ -81,6 +81,19 @@ where pub fn route_renderer(self) -> ChainProcess { ChainProcess::Ok((self, Next::Renderer)) } + + #[cfg(feature = "general_renderer")] + /// Restore AnyOutput back to the original Serialize type + pub fn restore(self) -> Option { + if self.type_id == std::any::TypeId::of::() { + match self.inner.downcast::() { + Ok(boxed) => Some(*boxed), + Err(_) => None, + } + } else { + None + } + } } impl std::ops::Deref for AnyOutput 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 Program @@ -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) -> bool; + + /// Perform general rendering and presentation of any type + #[cfg(feature = "general_renderer")] + fn general_render( + any: AnyOutput, + setting: &GeneralRendererSetting, + ) -> Result; } #[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 { + 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 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::(any)), + ChainProcess::Ok((any, Next::Renderer)) => { + return Ok(render::(&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::(any)), + ChainProcess::Ok((any, Next::Renderer)) => { + return Ok(render::(&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::(&program, current)); } // No renderer exists else { @@ -113,10 +115,28 @@ where } #[inline(always)] -fn render, G: Display>(any: AnyOutput) -> RenderResult { - let mut render_result = RenderResult::default(); - C::render(any, &mut render_result); - render_result +#[allow(unused_variables)] +fn render, G: Display>( + program: &Program, + any: AnyOutput, +) -> 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 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 ProgramSetup for GeneralRendererSimpleSetup +where + C: ProgramCollect, + G: Display, +{ + fn setup(&mut self, program: &mut Program) { + 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 ProgramSetup for GeneralRendererSetup +where + C: ProgramCollect, + G: Display, +{ + fn setup(&mut self, program: &mut Program) { + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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 for GeneralRendererSerializeError { + fn into(self) -> String { + self.error + } +} diff --git a/mingling_macros/Cargo.lock b/mingling_macros/Cargo.lock index 3291624..f65ae1f 100644 --- a/mingling_macros/Cargo.lock +++ b/mingling_macros/Cargo.lock @@ -10,7 +10,7 @@ checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e" [[package]] name = "mingling_macros" -version = "0.1.2" +version = "0.1.3" dependencies = [ "just_fmt", "once_cell", diff --git a/mingling_macros/Cargo.toml b/mingling_macros/Cargo.toml index d8631a3..8c42299 100644 --- a/mingling_macros/Cargo.toml +++ b/mingling_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mingling_macros" -version = "0.1.2" +version = "0.1.3" edition = "2024" license = "MIT OR Apache-2.0" repository = "https://github.com/catilgrass/mingling" @@ -11,7 +11,7 @@ proc-macro = true [features] default = [] -serde = [] +general_renderer = [] [dependencies] syn = { version = "2.0", features = ["full", "visit-mut"] } diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index f8b1e1c..7b519a1 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -93,11 +93,11 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { Err(e) => return e.to_compile_error().into(), }; - // Ensure the return type is named "GroupProcess" - if return_type.path.segments.last().unwrap().ident != "GroupProcess" { + // Ensure the return type is named "NextProcess" + if return_type.path.segments.last().unwrap().ident != "NextProcess" { return syn::Error::new( return_type.span(), - "Return type must be 'mingling::marker::GroupProcess'", + "Return type must be 'mingling::marker::NextProcess'", ) .to_compile_error() .into(); @@ -134,7 +134,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { async fn proc(#prev_param: Self::Previous) -> ::mingling::ChainProcess { - let _ = GroupProcess; + let _ = NextProcess; // Call the original function #fn_name(#prev_param).await } @@ -159,7 +159,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { async fn proc(#prev_param: Self::Previous) -> ::mingling::ChainProcess<#group_name> { - let _ = GroupProcess; + let _ = NextProcess; // Call the original function #fn_name(#prev_param).await } diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 5a32075..9513875 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -12,6 +12,7 @@ mod chain; mod dispatcher_chain; mod node; mod pack; +mod program_setup; mod render; mod renderer; @@ -19,6 +20,9 @@ use once_cell::sync::Lazy; use std::sync::Mutex; // Global variable declarations for storing chain and renderer mappings +#[cfg(feature = "general_renderer")] +pub(crate) static GENERAL_RENDERERS: Lazy>> = + Lazy::new(|| Mutex::new(Vec::new())); pub(crate) static PACKED_TYPES: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub(crate) static CHAINS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub(crate) static RENDERERS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); @@ -65,6 +69,11 @@ pub fn renderer(_attr: TokenStream, item: TokenStream) -> TokenStream { renderer::renderer_attr(item) } +#[proc_macro_attribute] +pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { + program_setup::setup_attr(attr, item) +} + #[proc_macro] pub fn gen_program(input: TokenStream) -> TokenStream { let name = if input.is_empty() { @@ -83,6 +92,9 @@ pub fn gen_program(input: TokenStream) -> TokenStream { let renderer_exist = RENDERERS_EXIST.lock().unwrap().clone(); let chain_exist = CHAINS_EXIST.lock().unwrap().clone(); + #[cfg(feature = "general_renderer")] + let general_renderers = GENERAL_RENDERERS.lock().unwrap().clone(); + let packed_types: Vec = packed_types .iter() .map(|s| syn::parse_str::(s).unwrap()) @@ -108,6 +120,28 @@ pub fn gen_program(input: TokenStream) -> TokenStream { .map(|s| syn::parse_str::(s).unwrap()) .collect(); + #[cfg(feature = "general_renderer")] + let general_renderer_tokens: Vec = general_renderers + .iter() + .map(|s| syn::parse_str::(s).unwrap()) + .collect(); + + #[cfg(feature = "general_renderer")] + let general_render = quote! { + fn general_render( + any: ::mingling::AnyOutput, + setting: &::mingling::GeneralRendererSetting, + ) -> Result<::mingling::RenderResult, ::mingling::error::GeneralRendererSerializeError> { + match any.member_id { + #(#general_renderer_tokens)* + _ => Ok(::mingling::RenderResult::default()), + } + } + }; + + #[cfg(not(feature = "general_renderer"))] + let general_render = quote! {}; + let expanded = quote! { ::mingling::macros::pack!(#name, RendererNotFound = String); ::mingling::macros::pack!(#name, DispatcherNotFound = Vec); @@ -155,6 +189,7 @@ pub fn gen_program(input: TokenStream) -> TokenStream { _ => false } } + #general_render } impl #name { diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs index c6a6c67..a84010e 100644 --- a/mingling_macros/src/pack.rs +++ b/mingling_macros/src/pack.rs @@ -77,7 +77,7 @@ pub fn pack(input: TokenStream) -> TokenStream { }; // Generate the struct definition - #[cfg(not(feature = "serde"))] + #[cfg(not(feature = "general_renderer"))] let struct_def = quote! { #[derive(Debug)] pub struct #type_name { @@ -85,7 +85,7 @@ pub fn pack(input: TokenStream) -> TokenStream { } }; - #[cfg(feature = "serde")] + #[cfg(feature = "general_renderer")] let struct_def = quote! { #[derive(Debug, serde::Serialize)] pub struct #type_name { diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs new file mode 100644 index 0000000..54da898 --- /dev/null +++ b/mingling_macros/src/program_setup.rs @@ -0,0 +1,199 @@ +//! Setup Attribute Macro Implementation +//! +//! This module provides the `#[setup]` attribute macro for automatically +//! generating structs that implement the `ProgramSetup` trait from functions. + +use proc_macro::TokenStream; +use quote::quote; +use syn::spanned::Spanned; +use syn::{ + FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input, +}; + +/// Extracts the program parameter from function arguments +fn extract_program_param(sig: &Signature) -> syn::Result<(Pat, TypePath)> { + // The function should have exactly one parameter + if sig.inputs.len() != 1 { + return Err(syn::Error::new( + sig.inputs.span(), + "Setup function must have exactly one parameter", + )); + } + + let arg = &sig.inputs[0]; + match arg { + FnArg::Typed(PatType { pat, ty, .. }) => { + // Extract the pattern (parameter name) + let param_pat = (**pat).clone(); + + // Extract the type, handling references like &mut ThisProgram + match &**ty { + Type::Path(type_path) => Ok((param_pat, type_path.clone())), + Type::Reference(type_ref) => { + // Handle reference types like &mut ThisProgram + match &*type_ref.elem { + Type::Path(type_path) => Ok((param_pat, type_path.clone())), + _ => Err(syn::Error::new( + ty.span(), + "Reference parameter must point to a type path", + )), + } + } + _ => Err(syn::Error::new( + ty.span(), + "Parameter type must be a type path or reference to a type path", + )), + } + } + FnArg::Receiver(_) => Err(syn::Error::new( + arg.span(), + "Setup function cannot have self parameter", + )), + } +} + +/// Validates that the parameter type is `ThisProgram` +fn validate_any_program_param(type_path: &TypePath) -> syn::Result<()> { + // Check if the type is `ThisProgram` + let segments = &type_path.path.segments; + if segments.len() == 1 && segments[0].ident == "ThisProgram" { + Ok(()) + } else { + // Check if it's a qualified path like mingling::marker::ThisProgram + let mut is_any_program = false; + if segments.len() > 1 { + // Check if the last segment is "ThisProgram" + if segments.last().unwrap().ident == "ThisProgram" { + is_any_program = true; + } + } + + if is_any_program { + Ok(()) + } else { + Err(syn::Error::new( + type_path.span(), + "Setup function parameter must be `mingling::marker::ThisProgram`", + )) + } + } +} + +/// Extracts and validates the return type +fn extract_return_type(sig: &Signature) -> syn::Result<()> { + // Setup functions should return () or have no return type + match &sig.output { + ReturnType::Type(_, ty) => { + // Check if it's () + match &**ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => Ok(()), + _ => Err(syn::Error::new( + ty.span(), + "Setup function must return () or have no return type", + )), + } + } + ReturnType::Default => Ok(()), + } +} + +pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { + // Parse the attribute arguments (e.g., MyProgram from #[setup(MyProgram)]) + // If no argument is provided, use DefaultProgram + let (program_name, use_crate_prefix) = if attr.is_empty() { + ( + Ident::new("DefaultProgram", proc_macro2::Span::call_site()), + true, + ) + } else { + (parse_macro_input!(attr as Ident), false) + }; + + // Parse the function item + let input_fn = parse_macro_input!(item as ItemFn); + + // Validate the function is not async + if input_fn.sig.asyncness.is_some() { + return syn::Error::new(input_fn.sig.span(), "Setup function cannot be async") + .to_compile_error() + .into(); + } + + // Extract the program parameter + let (program_param, program_type) = match extract_program_param(&input_fn.sig) { + Ok(info) => info, + Err(e) => return e.to_compile_error().into(), + }; + + // Validate that the parameter is ThisProgram + if let Err(e) = validate_any_program_param(&program_type) { + return e.to_compile_error().into(); + } + + // Validate return type + if let Err(e) = extract_return_type(&input_fn.sig) { + return e.to_compile_error().into(); + } + + // Get the function body + let fn_body = &input_fn.block; + + // Get function attributes (excluding the setup attribute) + let mut fn_attrs = input_fn.attrs.clone(); + + // Remove any #[setup(...)] attributes to avoid infinite recursion + fn_attrs.retain(|attr| !attr.path().is_ident("setup")); + + // Get function visibility + let vis = &input_fn.vis; + + // Get function name + let fn_name = &input_fn.sig.ident; + + // Generate struct name from function name using pascal_case + let pascal_case_name = just_fmt::pascal_case!(fn_name.to_string()); + let struct_name = Ident::new(&pascal_case_name, fn_name.span()); + + // Generate the struct and implementation + let expanded = if use_crate_prefix { + quote! { + #(#fn_attrs)* + #vis struct #struct_name; + + impl ::mingling::setup::ProgramSetup for #struct_name { + fn setup(&mut self, program: &mut ::mingling::Program) { + let _ = ThisProgram; + // Call the original function with the actual Program type + #fn_name(program); + } + } + + // Keep the original function for internal use + #(#fn_attrs)* + #vis fn #fn_name(#program_param: &mut ::mingling::Program) { + #fn_body + } + } + } else { + quote! { + #(#fn_attrs)* + #vis struct #struct_name; + + impl ::mingling::setup::ProgramSetup<#program_name, #program_name> for #struct_name { + fn setup(&mut self, program: &mut ::mingling::Program<#program_name, #program_name>) { + let _ = ThisProgram; + // Call the original function with the actual Program type + #fn_name(program); + } + } + + // Keep the original function for internal use + #(#fn_attrs)* + #vis fn #fn_name(#program_param: &mut ::mingling::Program<#program_name, #program_name>) { + #fn_body + } + } + }; + + expanded.into() +} diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs index 0e32b40..4edac88 100644 --- a/mingling_macros/src/renderer.rs +++ b/mingling_macros/src/renderer.rs @@ -109,14 +109,30 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream { Self::#previous_type => true, }; + #[cfg(feature = "general_renderer")] + let general_renderer_entry = quote! { + Self::#previous_type => { + let raw = any.restore::<#previous_type>().unwrap(); + let mut r = ::mingling::RenderResult::default(); + ::mingling::GeneralRenderer::render(&raw, setting, &mut r)?; + Ok(r) + } + }; + let mut renderers = crate::RENDERERS.lock().unwrap(); let mut renderer_exist = crate::RENDERERS_EXIST.lock().unwrap(); let mut packed_types = crate::PACKED_TYPES.lock().unwrap(); + #[cfg(feature = "general_renderer")] + let mut general_renderers = crate::GENERAL_RENDERERS.lock().unwrap(); + let renderer_entry_str = renderer_entry.to_string(); let renderer_exist_entry_str = renderer_exist_entry.to_string(); let previous_type_str = previous_type.to_token_stream().to_string(); + #[cfg(feature = "general_renderer")] + let general_renderer_entry_str = general_renderer_entry.to_string(); + if !renderers.contains(&renderer_entry_str) { renderers.push(renderer_entry_str); } @@ -129,6 +145,11 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream { packed_types.push(previous_type_str); } + #[cfg(feature = "general_renderer")] + if !general_renderers.contains(&general_renderer_entry_str) { + general_renderers.push(general_renderer_entry_str); + } + // Generate the struct and implementation // We need to create a wrapper function that adds the r parameter let expanded = quote! { -- cgit