summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mingling/Cargo.lock173
-rw-r--r--mingling/Cargo.toml2
-rw-r--r--mingling/src/input.rs2
-rw-r--r--mingling/src/input/input_field.rs1
-rw-r--r--mingling/src/lib.rs2
-rw-r--r--mingling_core/Cargo.lock177
-rw-r--r--mingling_core/Cargo.toml13
-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
-rw-r--r--mingling_macros/Cargo.lock2
-rw-r--r--mingling_macros/Cargo.toml4
-rw-r--r--mingling_macros/src/chain.rs10
-rw-r--r--mingling_macros/src/lib.rs35
-rw-r--r--mingling_macros/src/pack.rs4
-rw-r--r--mingling_macros/src/program_setup.rs199
-rw-r--r--mingling_macros/src/renderer.rs21
28 files changed, 960 insertions, 29 deletions
diff --git a/mingling/Cargo.lock b/mingling/Cargo.lock
index ef9be06..edb7731 100644
--- a/mingling/Cargo.lock
+++ b/mingling/Cargo.lock
@@ -3,18 +3,61 @@
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"
dependencies = [
@@ -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",
@@ -77,6 +124,26 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -107,6 +174,41 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -154,7 +256,76 @@ dependencies = [
]
[[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
@@ -3,28 +3,81 @@
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -49,6 +102,26 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,6 +152,41 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -120,7 +228,76 @@ dependencies = [
]
[[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<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
+ }
+}
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<DefaultProgram>
{
- 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<Mutex<Vec<String>>> =
+ Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static PACKED_TYPES: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static CHAINS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static RENDERERS: Lazy<Mutex<Vec<String>>> = 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<proc_macro2::TokenStream> = packed_types
.iter()
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
@@ -108,6 +120,28 @@ pub fn gen_program(input: TokenStream) -> TokenStream {
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
.collect();
+ #[cfg(feature = "general_renderer")]
+ let general_renderer_tokens: Vec<proc_macro2::TokenStream> = general_renderers
+ .iter()
+ .map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
+ .collect();
+
+ #[cfg(feature = "general_renderer")]
+ let general_render = quote! {
+ fn general_render(
+ any: ::mingling::AnyOutput<Self::Enum>,
+ 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<String>);
@@ -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<DefaultProgram, DefaultProgram> for #struct_name {
+ fn setup(&mut self, program: &mut ::mingling::Program<DefaultProgram, DefaultProgram>) {
+ 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<DefaultProgram, DefaultProgram>) {
+ #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! {