summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mingling/src/lib.rs2
-rw-r--r--mingling_core/src/any.rs56
-rw-r--r--mingling_core/src/any/group.rs3
-rw-r--r--mingling_core/src/asset/chain.rs9
-rw-r--r--mingling_core/src/asset/chain/error.rs5
-rw-r--r--mingling_core/src/asset/dispatcher.rs140
-rw-r--r--mingling_core/src/lib.rs9
-rw-r--r--mingling_core/src/markers.rs1
-rw-r--r--mingling_core/src/markers/group_process.rs2
-rw-r--r--mingling_core/src/program.rs57
-rw-r--r--mingling_core/src/program/exec.rs69
-rw-r--r--mingling_core/src/program/exec/error.rs12
-rw-r--r--mingling_core/src/program/flag.rs9
-rw-r--r--mingling_core/src/program/hint.rs62
-rw-r--r--mingling_core/src/program/setup.rs15
-rw-r--r--mingling_core/src/program/setup/basic.rs7
-rw-r--r--mingling_macros/src/README.txt12
-rw-r--r--mingling_macros/src/chain.rs97
-rw-r--r--mingling_macros/src/dispatcher_chain.rs219
-rw-r--r--mingling_macros/src/lib.rs58
-rw-r--r--mingling_macros/src/pack.rs158
-rw-r--r--mingling_macros/src/renderer.rs11
22 files changed, 679 insertions, 334 deletions
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 5c5ae7d..86e1152 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -10,9 +10,9 @@ pub mod macros {
pub use mingling_macros::chain;
pub use mingling_macros::dispatcher;
pub use mingling_macros::dispatcher_render;
+ pub use mingling_macros::gen_program;
pub use mingling_macros::node;
pub use mingling_macros::pack;
- pub use mingling_macros::program;
pub use mingling_macros::r_print;
pub use mingling_macros::r_println;
pub use mingling_macros::renderer;
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index 1bce96a..dac0d44 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -1,34 +1,48 @@
+use std::fmt::Display;
+
#[cfg(feature = "general_renderer")]
use serde::Serialize;
+use crate::Groupped;
use crate::error::ChainProcessError;
+pub mod group;
+
#[derive(Debug)]
-pub struct AnyOutput {
+pub struct AnyOutput<G>
+where
+ G: Display,
+{
inner: Box<dyn std::any::Any + Send + 'static>,
pub type_id: std::any::TypeId,
+ pub member_id: G,
}
-impl AnyOutput {
+impl<G> AnyOutput<G>
+where
+ G: Display,
+{
#[cfg(feature = "general_renderer")]
pub fn new<T>(value: T) -> Self
where
- T: Send + Serialize + 'static,
+ T: Send + Groupped<G> + Serialize + 'static,
{
Self {
inner: Box::new(value),
type_id: std::any::TypeId::of::<T>(),
+ member_id: T::member_id(),
}
}
#[cfg(not(feature = "general_renderer"))]
pub fn new<T>(value: T) -> Self
where
- T: Send + 'static,
+ T: Send + Groupped<G> + 'static,
{
Self {
inner: Box::new(value),
type_id: std::any::TypeId::of::<T>(),
+ member_id: T::member_id(),
}
}
@@ -45,17 +59,20 @@ impl AnyOutput {
}
/// Route the output to the next Chain
- pub fn route_chain(self) -> ChainProcess {
+ pub fn route_chain(self) -> ChainProcess<G> {
ChainProcess::Ok((self, Next::Chain))
}
/// Route the output to the Renderer, ending execution
- pub fn route_renderer(self) -> ChainProcess {
+ pub fn route_renderer(self) -> ChainProcess<G> {
ChainProcess::Ok((self, Next::Renderer))
}
}
-impl std::ops::Deref for AnyOutput {
+impl<G> std::ops::Deref for AnyOutput<G>
+where
+ G: Display,
+{
type Target = dyn std::any::Any + Send + 'static;
fn deref(&self) -> &Self::Target {
@@ -63,14 +80,20 @@ impl std::ops::Deref for AnyOutput {
}
}
-impl std::ops::DerefMut for AnyOutput {
+impl<G> std::ops::DerefMut for AnyOutput<G>
+where
+ G: Display,
+{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.inner
}
}
-pub enum ChainProcess {
- Ok((AnyOutput, Next)),
+pub enum ChainProcess<G>
+where
+ G: Display,
+{
+ Ok((AnyOutput<G>, Next)),
Err(ChainProcessError),
}
@@ -79,7 +102,10 @@ pub enum Next {
Renderer,
}
-impl ChainProcess {
+impl<G> ChainProcess<G>
+where
+ G: Display,
+{
pub fn is_next(&self) -> bool {
matches!(self, Self::Ok(_))
}
@@ -102,23 +128,23 @@ impl ChainProcess {
}
}
- pub fn unwrap(self) -> (AnyOutput, Next) {
+ pub fn unwrap(self) -> (AnyOutput<G>, Next) {
match self {
Self::Ok(tuple) => tuple,
Self::Err(_) => panic!("called `ChainProcess2::unwrap()` on an `Error` value"),
}
}
- pub fn unwrap_or(self, default: (AnyOutput, Next)) -> (AnyOutput, Next) {
+ pub fn unwrap_or(self, default: (AnyOutput<G>, Next)) -> (AnyOutput<G>, Next) {
match self {
Self::Ok(tuple) => tuple,
Self::Err(_) => default,
}
}
- pub fn unwrap_or_else<F>(self, f: F) -> (AnyOutput, Next)
+ pub fn unwrap_or_else<F>(self, f: F) -> (AnyOutput<G>, Next)
where
- F: FnOnce(ChainProcessError) -> (AnyOutput, Next),
+ F: FnOnce(ChainProcessError) -> (AnyOutput<G>, Next),
{
match self {
Self::Ok(tuple) => tuple,
diff --git a/mingling_core/src/any/group.rs b/mingling_core/src/any/group.rs
new file mode 100644
index 0000000..29d6a48
--- /dev/null
+++ b/mingling_core/src/any/group.rs
@@ -0,0 +1,3 @@
+pub trait Groupped<Group> {
+ fn member_id() -> Group;
+}
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs
index 1ea1125..9f62228 100644
--- a/mingling_core/src/asset/chain.rs
+++ b/mingling_core/src/asset/chain.rs
@@ -1,8 +1,13 @@
+use std::fmt::Display;
+
use crate::ChainProcess;
pub mod error;
-pub trait Chain {
+pub trait Chain<G>
+where
+ G: Display,
+{
type Previous;
- fn proc(p: Self::Previous) -> impl Future<Output = ChainProcess> + Send;
+ fn proc(p: Self::Previous) -> impl Future<Output = ChainProcess<G>> + Send;
}
diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs
index d4da4ac..cc22bdc 100644
--- a/mingling_core/src/asset/chain/error.rs
+++ b/mingling_core/src/asset/chain/error.rs
@@ -1,5 +1,3 @@
-use crate::AnyOutput;
-
#[derive(thiserror::Error, Debug)]
pub enum ChainProcessError {
#[error("Other error: {0}")]
@@ -7,7 +5,4 @@ pub enum ChainProcessError {
#[error("IO error: {0}")]
IO(#[from] std::io::Error),
-
- #[error("Broken chain")]
- Broken(AnyOutput),
}
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index 13e35f7..0863f16 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -1,60 +1,66 @@
+use std::fmt::Display;
+
use crate::{ChainProcess, Program, asset::node::Node};
-pub trait Dispatcher {
+pub trait Dispatcher<G>
+where
+ G: Display,
+{
fn node(&self) -> Node;
- fn begin(&self, args: Vec<String>) -> ChainProcess;
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher>;
+ fn begin(&self, args: Vec<String>) -> ChainProcess<G>;
+ fn clone_dispatcher(&self) -> Box<dyn Dispatcher<G>>;
}
-impl Clone for Box<dyn Dispatcher> {
+impl<G> Clone for Box<dyn Dispatcher<G>>
+where
+ G: Display,
+{
fn clone(&self) -> Self {
self.clone_dispatcher()
}
}
-impl<C: crate::program::ProgramCollect> Program<C> {
+impl<C: crate::program::ProgramCollect, G: Display> Program<C, G> {
/// Adds a dispatcher to the program.
- pub fn with_dispatcher<D>(&mut self, dispatcher: D)
+ pub fn with_dispatcher<Disp>(&mut self, dispatcher: Disp)
where
- D: Into<Dispatchers>,
+ Disp: Dispatcher<G> + 'static,
{
- let dispatchers = dispatcher.into().dispatcher;
- self.dispatcher.extend(dispatchers);
+ self.dispatcher.push(Box::new(dispatcher));
}
-}
-pub struct Dispatchers {
- dispatcher: Vec<Box<dyn Dispatcher + 'static>>,
+ /// Add some dispatchers to the program.
+ pub fn with_dispatchers<D>(&mut self, dispatchers: D)
+ where
+ D: Into<Dispatchers<G>>,
+ {
+ let dispatchers = dispatchers.into();
+ self.dispatcher.extend(dispatchers.dispatcher);
+ }
}
-impl<D> From<D> for Dispatchers
-where
- D: Dispatcher + 'static,
-{
- fn from(dispatcher: D) -> Self {
- Self {
- dispatcher: vec![Box::new(dispatcher)],
- }
- }
+pub struct Dispatchers<G> {
+ dispatcher: Vec<Box<dyn Dispatcher<G> + 'static>>,
}
-impl From<Vec<Box<dyn Dispatcher>>> for Dispatchers {
- fn from(dispatcher: Vec<Box<dyn Dispatcher>>) -> Self {
+impl<G> From<Vec<Box<dyn Dispatcher<G>>>> for Dispatchers<G> {
+ fn from(dispatcher: Vec<Box<dyn Dispatcher<G>>>) -> Self {
Self { dispatcher }
}
}
-impl From<Box<dyn Dispatcher>> for Dispatchers {
- fn from(dispatcher: Box<dyn Dispatcher>) -> Self {
+impl<G> From<Box<dyn Dispatcher<G>>> for Dispatchers<G> {
+ fn from(dispatcher: Box<dyn Dispatcher<G>>) -> Self {
Self {
dispatcher: vec![dispatcher],
}
}
}
-impl<D> From<(D,)> for Dispatchers
+impl<D, G> From<(D,)> for Dispatchers<G>
where
- D: Dispatcher + 'static,
+ D: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatcher: (D,)) -> Self {
Self {
@@ -63,10 +69,11 @@ where
}
}
-impl<D1, D2> From<(D1, D2)> for Dispatchers
+impl<D1, D2, G> From<(D1, D2)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2)) -> Self {
Self {
@@ -75,11 +82,12 @@ where
}
}
-impl<D1, D2, D3> From<(D1, D2, D3)> for Dispatchers
+impl<D1, D2, D3, G> From<(D1, D2, D3)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
- D3: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ D3: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2, D3)) -> Self {
Self {
@@ -92,12 +100,13 @@ where
}
}
-impl<D1, D2, D3, D4> From<(D1, D2, D3, D4)> for Dispatchers
+impl<D1, D2, D3, D4, G> From<(D1, D2, D3, D4)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
- D3: Dispatcher + 'static,
- D4: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ D3: Dispatcher<G> + 'static,
+ D4: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2, D3, D4)) -> Self {
Self {
@@ -111,13 +120,14 @@ where
}
}
-impl<D1, D2, D3, D4, D5> From<(D1, D2, D3, D4, D5)> for Dispatchers
+impl<D1, D2, D3, D4, D5, G> From<(D1, D2, D3, D4, D5)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
- D3: Dispatcher + 'static,
- D4: Dispatcher + 'static,
- D5: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ D3: Dispatcher<G> + 'static,
+ D4: Dispatcher<G> + 'static,
+ D5: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2, D3, D4, D5)) -> Self {
Self {
@@ -132,14 +142,15 @@ where
}
}
-impl<D1, D2, D3, D4, D5, D6> From<(D1, D2, D3, D4, D5, D6)> for Dispatchers
+impl<D1, D2, D3, D4, D5, D6, G> From<(D1, D2, D3, D4, D5, D6)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
- D3: Dispatcher + 'static,
- D4: Dispatcher + 'static,
- D5: Dispatcher + 'static,
- D6: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ D3: Dispatcher<G> + 'static,
+ D4: Dispatcher<G> + 'static,
+ D5: Dispatcher<G> + 'static,
+ D6: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2, D3, D4, D5, D6)) -> Self {
Self {
@@ -155,15 +166,16 @@ where
}
}
-impl<D1, D2, D3, D4, D5, D6, D7> From<(D1, D2, D3, D4, D5, D6, D7)> for Dispatchers
+impl<D1, D2, D3, D4, D5, D6, D7, G> From<(D1, D2, D3, D4, D5, D6, D7)> for Dispatchers<G>
where
- D1: Dispatcher + 'static,
- D2: Dispatcher + 'static,
- D3: Dispatcher + 'static,
- D4: Dispatcher + 'static,
- D5: Dispatcher + 'static,
- D6: Dispatcher + 'static,
- D7: Dispatcher + 'static,
+ D1: Dispatcher<G> + 'static,
+ D2: Dispatcher<G> + 'static,
+ D3: Dispatcher<G> + 'static,
+ D4: Dispatcher<G> + 'static,
+ D5: Dispatcher<G> + 'static,
+ D6: Dispatcher<G> + 'static,
+ D7: Dispatcher<G> + 'static,
+ G: Display,
{
fn from(dispatchers: (D1, D2, D3, D4, D5, D6, D7)) -> Self {
Self {
@@ -180,16 +192,16 @@ where
}
}
-impl std::ops::Deref for Dispatchers {
- type Target = Vec<Box<dyn Dispatcher + 'static>>;
+impl<G> std::ops::Deref for Dispatchers<G> {
+ type Target = Vec<Box<dyn Dispatcher<G> + 'static>>;
fn deref(&self) -> &Self::Target {
&self.dispatcher
}
}
-impl From<Dispatchers> for Vec<Box<dyn Dispatcher + 'static>> {
- fn from(val: Dispatchers) -> Self {
+impl<G> From<Dispatchers<G>> for Vec<Box<dyn Dispatcher<G> + 'static>> {
+ fn from(val: Dispatchers<G>) -> Self {
val.dispatcher
}
}
diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs
index 10a1830..3d2be11 100644
--- a/mingling_core/src/lib.rs
+++ b/mingling_core/src/lib.rs
@@ -1,6 +1,12 @@
mod any;
+pub use crate::any::group::*;
pub use crate::any::*;
+mod markers;
+pub mod marker {
+ pub use crate::markers::group_process::*;
+}
+
pub mod error {
pub use crate::asset::chain::error::*;
pub use crate::exec::error::*;
@@ -11,9 +17,6 @@ pub use crate::program::*;
pub mod setup {
pub use crate::program::setup::*;
}
-pub mod hint {
- pub use crate::program::hint::*;
-}
mod renderer;
diff --git a/mingling_core/src/markers.rs b/mingling_core/src/markers.rs
new file mode 100644
index 0000000..e95fb8a
--- /dev/null
+++ b/mingling_core/src/markers.rs
@@ -0,0 +1 @@
+pub mod group_process;
diff --git a/mingling_core/src/markers/group_process.rs b/mingling_core/src/markers/group_process.rs
new file mode 100644
index 0000000..c9176f4
--- /dev/null
+++ b/mingling_core/src/markers/group_process.rs
@@ -0,0 +1,2 @@
+#[allow(dead_code)]
+pub struct GroupProcess;
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index b13a879..1c8c0b4 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -2,10 +2,9 @@ use crate::{
AnyOutput, ChainProcess, RenderResult, asset::dispatcher::Dispatcher,
error::ProgramExecuteError,
};
-use std::{env, pin::Pin};
+use std::{env, fmt::Display, pin::Pin};
pub mod exec;
-pub mod hint;
pub mod setup;
mod config;
@@ -16,24 +15,31 @@ pub use flag::*;
use tokio::io::AsyncWriteExt;
#[derive(Default)]
-pub struct Program<C: ProgramCollect> {
+pub struct Program<C, G>
+where
+ C: ProgramCollect,
+ G: Display,
+{
pub(crate) collect: std::marker::PhantomData<C>,
+ pub(crate) group: std::marker::PhantomData<G>,
pub(crate) args: Vec<String>,
- pub(crate) dispatcher: Vec<Box<dyn Dispatcher>>,
+ pub(crate) dispatcher: Vec<Box<dyn Dispatcher<G>>>,
pub stdout_setting: ProgramStdoutSetting,
pub user_context: ProgramUserContext,
}
-impl<C> Program<C>
+impl<C, G> Program<C, G>
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = G>,
+ G: Display,
{
/// Creates a new Program instance, initializing args from environment.
pub fn new() -> Self {
Program {
collect: std::marker::PhantomData,
+ group: std::marker::PhantomData,
args: env::args().collect(),
dispatcher: Vec::new(),
stdout_setting: Default::default(),
@@ -57,6 +63,10 @@ where
eprintln!("Dispatcher not found");
return;
}
+ ProgramExecuteError::RendererNotFound(renderer_name) => {
+ eprintln!("Renderer `{}` not found", renderer_name);
+ return;
+ }
ProgramExecuteError::Other(e) => {
eprintln!("{}", e);
return;
@@ -77,24 +87,27 @@ where
}
pub trait ProgramCollect {
- fn render(any: AnyOutput, r: &mut RenderResult);
- fn do_chain(any: AnyOutput) -> Pin<Box<dyn Future<Output = ChainProcess> + Send>>;
- fn has_renderer(any: &AnyOutput) -> bool;
- fn has_chain(any: &AnyOutput) -> bool;
+ type Enum: Display;
+ fn render(any: AnyOutput<Self::Enum>, r: &mut RenderResult);
+ fn do_chain(
+ any: AnyOutput<Self::Enum>,
+ ) -> Pin<Box<dyn Future<Output = ChainProcess<Self::Enum>> + Send>>;
+ fn has_renderer(any: &AnyOutput<Self::Enum>) -> bool;
+ fn has_chain(any: &AnyOutput<Self::Enum>) -> bool;
}
#[macro_export]
#[doc(hidden)]
macro_rules! __dispatch_program_renderers {
(
- $( $render_ty:ty => $prev_ty:ty, )*
+ $( $render_ty:ty => $prev_ty:ident, )*
) => {
- fn render(any: mingling::AnyOutput, r: &mut mingling::RenderResult) {
- match any.type_id {
+ fn render(any: mingling::AnyOutput<Self::Enum>, r: &mut mingling::RenderResult) {
+ match any.member_id {
$(
- id if id == std::any::TypeId::of::<$prev_ty>() => {
- // SAFETY: The `type_id` check ensures that `any` contains a value of type `$chain_prev`,
- // so downcasting to `$chain_prev` is safe.
+ Self::$prev_ty => {
+ // SAFETY: The `type_id` check ensures that `any` contains a value of type `$prev_ty`,
+ // so downcasting to `$prev_ty` is safe.
let value = unsafe { any.downcast::<$prev_ty>().unwrap_unchecked() };
<$render_ty as mingling::Renderer>::render(value, r);
}
@@ -109,18 +122,18 @@ macro_rules! __dispatch_program_renderers {
#[doc(hidden)]
macro_rules! __dispatch_program_chains {
(
- $( $chain_ty:ty => $chain_prev:ty, )*
+ $( $chain_ty:ty => $chain_prev:ident, )*
) => {
fn do_chain(
- any: mingling::AnyOutput,
- ) -> std::pin::Pin<Box<dyn Future<Output = mingling::ChainProcess> + Send>> {
- match any.type_id {
+ any: mingling::AnyOutput<Self::Enum>,
+ ) -> std::pin::Pin<Box<dyn Future<Output = mingling::ChainProcess<Self::Enum>> + Send>> {
+ match any.member_id {
$(
- id if id == std::any::TypeId::of::<$chain_prev>() => {
+ Self::$chain_prev => {
// SAFETY: The `type_id` check ensures that `any` contains a value of type `$chain_prev`,
// so downcasting to `$chain_prev` is safe.
let value = unsafe { any.downcast::<$chain_prev>().unwrap_unchecked() };
- let fut = async { <$chain_ty as mingling::Chain>::proc(value).await };
+ let fut = async { <$chain_ty as mingling::Chain<Self::Enum>>::proc(value).await };
Box::pin(fut)
}
)*
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 853bff1..cdbb2b0 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -1,42 +1,44 @@
#![allow(clippy::borrowed_box)]
+use std::fmt::Display;
+
use crate::{
AnyOutput, ChainProcess, Dispatcher, Next, Program, ProgramCollect, RenderResult,
error::ProgramInternalExecuteError,
- hint::{DispatcherNotFound, RendererNotFound},
};
pub mod error;
-pub async fn exec<C: ProgramCollect>(
- program: Program<C>,
-) -> Result<RenderResult, ProgramInternalExecuteError> {
+pub async fn exec<C, G>(program: Program<C, G>) -> Result<RenderResult, ProgramInternalExecuteError>
+where
+ C: ProgramCollect<Enum = G>,
+ G: Display,
+{
+ let mut current;
+
// Match user input
- let matched: (Box<dyn Dispatcher>, Vec<String>) = match match_user_input(&program) {
- Ok(r) => (r.0.clone(), r.1),
+ match match_user_input(&program) {
+ 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::Chain)) => any,
+ ChainProcess::Err(e) => return Err(e.into()),
+ };
+ }
Err(ProgramInternalExecuteError::DispatcherNotFound) => {
- // If no Dispatcher is found, dispatch to the DispatcherNotFound Dispatcher
- // to route it to the NoDispatcherFound struct
- let disp: Box<dyn Dispatcher> = Box::new(DispatcherNotFound);
- (disp, program.args)
+ // No matching Dispatcher is found
+ return Err(ProgramInternalExecuteError::DispatcherNotFound);
}
Err(e) => return Err(e),
};
- // Entry point
- let (dispatcher, args) = matched;
- let mut current = match dispatcher.begin(args) {
- ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C>(any)),
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
- };
-
loop {
current = {
// 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>(any)),
+ ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C, G>(any)),
ChainProcess::Ok((any, Next::Chain)) => any,
ChainProcess::Err(e) => return Err(e.into()),
}
@@ -47,29 +49,28 @@ pub async fn exec<C: ProgramCollect>(
C::render(current, &mut render_result);
return Ok(render_result);
}
- // If no renderer exists, transfer to the RendererNotFound Dispatcher for execution
+ // No renderer exists
else {
- let disp: Box<dyn Dispatcher> = Box::new(RendererNotFound);
-
- match disp.begin(vec![format!("{:?}", current.type_id)]) {
- ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C>(any)),
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
- }
+ let renderer_name = current.member_id.to_string();
+ return Err(ProgramInternalExecuteError::RendererNotFound(renderer_name));
}
};
}
}
/// Match user input against registered dispatchers and return the matched dispatcher and remaining arguments.
-fn match_user_input<C: ProgramCollect>(
- program: &Program<C>,
-) -> Result<(&Box<dyn Dispatcher>, Vec<String>), ProgramInternalExecuteError> {
+fn match_user_input<C, G>(
+ program: &Program<C, G>,
+) -> Result<(&Box<dyn Dispatcher<G>>, Vec<String>), ProgramInternalExecuteError>
+where
+ C: ProgramCollect<Enum = G>,
+ G: Display,
+{
let nodes = get_nodes(program);
let command = format!("{} ", program.args.join(" "));
// Find all nodes that match the command prefix
- let matching_nodes: Vec<&(String, &Box<dyn Dispatcher>)> = nodes
+ let matching_nodes: Vec<&(String, &Box<dyn Dispatcher<G>>)> = nodes
.iter()
// Also add a space to the node string to ensure consistent matching logic
.filter(|(node_str, _)| command.starts_with(&format!("{} ", node_str)))
@@ -102,14 +103,16 @@ fn match_user_input<C: ProgramCollect>(
}
#[inline(always)]
-fn render<C: ProgramCollect>(any: AnyOutput) -> RenderResult {
+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
}
// Get all registered dispatcher names from the program
-fn get_nodes<C: ProgramCollect>(program: &Program<C>) -> Vec<(String, &Box<dyn Dispatcher>)> {
+fn get_nodes<C: ProgramCollect<Enum = G>, G: Display>(
+ program: &Program<C, G>,
+) -> Vec<(String, &Box<dyn Dispatcher<G>>)> {
program
.dispatcher
.iter()
diff --git a/mingling_core/src/program/exec/error.rs b/mingling_core/src/program/exec/error.rs
index fe66e22..b4ff378 100644
--- a/mingling_core/src/program/exec/error.rs
+++ b/mingling_core/src/program/exec/error.rs
@@ -5,6 +5,9 @@ pub enum ProgramExecuteError {
#[error("No Dispatcher Found")]
DispatcherNotFound,
+ #[error("No Renderer (`{0}`) Found")]
+ RendererNotFound(String),
+
#[error("Other error: {0}")]
Other(String),
}
@@ -14,6 +17,9 @@ pub enum ProgramInternalExecuteError {
#[error("No Dispatcher Found")]
DispatcherNotFound,
+ #[error("No Renderer (`{0}`) Found")]
+ RendererNotFound(String),
+
#[error("Other error: {0}")]
Other(String),
@@ -27,6 +33,9 @@ impl From<ProgramInternalExecuteError> for ProgramExecuteError {
ProgramInternalExecuteError::DispatcherNotFound => {
ProgramExecuteError::DispatcherNotFound
}
+ ProgramInternalExecuteError::RendererNotFound(s) => {
+ ProgramExecuteError::RendererNotFound(s)
+ }
ProgramInternalExecuteError::Other(s) => ProgramExecuteError::Other(s),
ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{}", e)),
}
@@ -38,9 +47,6 @@ impl From<ChainProcessError> for ProgramInternalExecuteError {
match value {
ChainProcessError::Other(s) => ProgramInternalExecuteError::Other(s),
ChainProcessError::IO(error) => ProgramInternalExecuteError::IO(error),
- ChainProcessError::Broken(_) => {
- ProgramInternalExecuteError::Other("Broken".to_string())
- }
}
}
}
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 74435e4..84fae01 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
use crate::{Program, ProgramCollect};
pub struct Flag {
@@ -88,14 +90,15 @@ macro_rules! special_argument {
}};
}
-impl<C> Program<C>
+impl<C, G> Program<C, G>
where
C: ProgramCollect,
+ G: Display,
{
/// Registers a global argument (with value) and its handler.
pub fn global_argument<F, A>(&mut self, arguments: A, do_fn: F)
where
- F: Fn(&mut Program<C>, String),
+ F: Fn(&mut Program<C, G>, String),
A: Into<Flag>,
{
let flag = arguments.into();
@@ -111,7 +114,7 @@ where
/// Registers a global flag (boolean) and its handler.
pub fn global_flag<F, A>(&mut self, flag: A, do_fn: F)
where
- F: Fn(&mut Program<C>),
+ F: Fn(&mut Program<C, G>),
A: Into<Flag>,
{
let flag = flag.into();
diff --git a/mingling_core/src/program/hint.rs b/mingling_core/src/program/hint.rs
deleted file mode 100644
index 6dbbac2..0000000
--- a/mingling_core/src/program/hint.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use crate::{AnyOutput, ChainProcess, Dispatcher, Node};
-
-/// Marker: Dispatcher Not Found
-///
-/// If a Dispatcher outputs NoDispatcherFound to the Chain,
-/// the program will terminate directly.
-///
-/// You can implement Renderer for NoDispatcherFound
-/// to render relevant information when a Dispatcher cannot be found.
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct NoDispatcherFound {
- pub args: Vec<String>,
-}
-
-#[derive(Default)]
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct DispatcherNotFound;
-impl Dispatcher for DispatcherNotFound {
- fn node(&self) -> crate::Node {
- Node::default().join("_not_found")
- }
-
- fn begin(&self, args: Vec<String>) -> ChainProcess {
- AnyOutput::new(NoDispatcherFound { args }).route_renderer()
- }
-
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher> {
- Box::new(DispatcherNotFound)
- }
-}
-
-/// Marker: Renderer Not Found
-///
-/// If a Chain outputs NoRendererFound to the Chain,
-/// the program will terminate directly.
-///
-/// You can implement Renderer for NoRendererFound
-/// to render relevant information when a Renderer cannot be found.
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct NoRendererFound {
- pub type_to_render: String,
-}
-
-#[derive(Default)]
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct RendererNotFound;
-impl Dispatcher for RendererNotFound {
- fn node(&self) -> crate::Node {
- Node::default().join("_not_found")
- }
-
- fn begin(&self, args: Vec<String>) -> ChainProcess {
- AnyOutput::new(NoRendererFound {
- type_to_render: args.first().unwrap().clone(),
- })
- .route_renderer()
- }
-
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher> {
- Box::new(RendererNotFound)
- }
-}
diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs
index e81247e..1f16f80 100644
--- a/mingling_core/src/program/setup.rs
+++ b/mingling_core/src/program/setup.rs
@@ -1,18 +1,25 @@
+use std::fmt::Display;
+
use crate::{ProgramCollect, program::Program};
mod basic;
pub use basic::*;
-pub trait ProgramSetup<C: ProgramCollect> {
- fn setup(&mut self, program: &mut Program<C>);
+pub trait ProgramSetup<C, G>
+where
+ C: ProgramCollect,
+ G: Display,
+{
+ fn setup(&mut self, program: &mut Program<C, G>);
}
-impl<C> Program<C>
+impl<C, G> Program<C, G>
where
C: ProgramCollect,
+ G: Display,
{
/// Load and execute init logic
- pub fn with_setup<S: ProgramSetup<C> + 'static>(&mut self, mut setup: S) -> S {
+ pub fn with_setup<S: ProgramSetup<C, G> + 'static>(&mut self, mut setup: S) -> S {
S::setup(&mut setup, self);
setup
}
diff --git a/mingling_core/src/program/setup/basic.rs b/mingling_core/src/program/setup/basic.rs
index 43c14b9..8316a33 100644
--- a/mingling_core/src/program/setup/basic.rs
+++ b/mingling_core/src/program/setup/basic.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
use crate::{
ProgramCollect,
program::{Program, setup::ProgramSetup},
@@ -10,11 +12,12 @@ use crate::{
/// - Collects `--confirm` flag to skip user confirmation
pub struct BasicProgramSetup;
-impl<C> ProgramSetup<C> for BasicProgramSetup
+impl<C, G> ProgramSetup<C, G> for BasicProgramSetup
where
C: ProgramCollect,
+ G: Display,
{
- fn setup(&mut self, program: &mut Program<C>) {
+ fn setup(&mut self, program: &mut Program<C, G>) {
program.global_flag(["--quiet", "-q"], |p| {
p.stdout_setting.render_output = false;
p.stdout_setting.error_output = false;
diff --git a/mingling_macros/src/README.txt b/mingling_macros/src/README.txt
new file mode 100644
index 0000000..a882bd0
--- /dev/null
+++ b/mingling_macros/src/README.txt
@@ -0,0 +1,12 @@
+*Listen up*, this is a messy macro written by AI.
+It works, but it looks like expired spaghetti and smells like freshly laid shit.
+
+But the general idea is mostly right. If I'm in a good mood, this code will definitely need a refactor.
+
+----------------------------------------------------
+Chinese original version:
+
+*听着*,这是一坨由 AI 写的乱七八糟的宏
+它能用,但是看起来就像过期的意大利面,闻起来就像刚拉出来的新鲜的屎
+
+不过思路大抵是对的,如果心情好这坨代码肯定要重构
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs
index 1fddd3b..f8b1e1c 100644
--- a/mingling_macros/src/chain.rs
+++ b/mingling_macros/src/chain.rs
@@ -1,10 +1,10 @@
//! Chain Attribute Macro Implementation
//!
-//! This module provides the `#[chain]` attribute macro for automatically
+//! This module provides the `#[chain(Group)]` attribute macro for automatically
//! generating structs that implement the `Chain` trait from async functions.
use proc_macro::TokenStream;
-use quote::quote;
+use quote::{ToTokens, quote};
use syn::spanned::Spanned;
use syn::{
FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input,
@@ -59,7 +59,18 @@ fn extract_return_type(sig: &Signature) -> syn::Result<TypePath> {
}
}
-pub fn chain_attr(item: TokenStream) -> TokenStream {
+pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
+ // Parse the attribute arguments (e.g., MyProgram from #[chain(MyProgram)])
+ // If no argument is provided, use DefaultProgram
+ let (group_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);
@@ -82,11 +93,22 @@ pub fn chain_attr(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" {
+ return syn::Error::new(
+ return_type.span(),
+ "Return type must be 'mingling::marker::GroupProcess'",
+ )
+ .to_compile_error()
+ .into();
+ }
+
// Get the function body
let fn_body = &input_fn.block;
// Get function attributes (excluding the chain attribute)
let mut fn_attrs = input_fn.attrs.clone();
+
// Remove any #[chain(...)] attributes to avoid infinite recursion
fn_attrs.retain(|attr| !attr.path().is_ident("chain"));
@@ -101,23 +123,55 @@ pub fn chain_attr(item: TokenStream) -> TokenStream {
let struct_name = Ident::new(&pascal_case_name, fn_name.span());
// Generate the struct and implementation
- let expanded = quote! {
- #(#fn_attrs)*
- #vis struct #struct_name;
-
- impl ::mingling::Chain for #struct_name {
- type Previous = #previous_type;
+ let expanded = if use_crate_prefix {
+ quote! {
+ #(#fn_attrs)*
+ #vis struct #struct_name;
+
+ impl ::mingling::Chain<DefaultProgram> for #struct_name {
+ type Previous = #previous_type;
+
+ async fn proc(#prev_param: Self::Previous) ->
+ ::mingling::ChainProcess<DefaultProgram>
+ {
+ let _ = GroupProcess;
+ // Call the original function
+ #fn_name(#prev_param).await
+ }
+ }
- async fn proc(#prev_param: Self::Previous) -> #return_type {
- // Call the original function
- #fn_name(#prev_param).await
+ // Keep the original function for internal use
+ #(#fn_attrs)*
+ #vis async fn #fn_name(#prev_param: #previous_type)
+ -> ::mingling::ChainProcess<DefaultProgram>
+ {
+ #fn_body
}
}
+ } else {
+ quote! {
+ #(#fn_attrs)*
+ #vis struct #struct_name;
+
+ impl ::mingling::Chain<#group_name> for #struct_name {
+ type Previous = #previous_type;
+
+ async fn proc(#prev_param: Self::Previous) ->
+ ::mingling::ChainProcess<#group_name>
+ {
+ let _ = GroupProcess;
+ // Call the original function
+ #fn_name(#prev_param).await
+ }
+ }
- // Keep the original function for internal use
- #(#fn_attrs)*
- #vis async fn #fn_name(#prev_param: #previous_type) -> #return_type {
- #fn_body
+ // Keep the original function for internal use
+ #(#fn_attrs)*
+ #vis async fn #fn_name(#prev_param: #previous_type)
+ -> ::mingling::ChainProcess<#group_name>
+ {
+ #fn_body
+ }
}
};
@@ -127,21 +181,28 @@ pub fn chain_attr(item: TokenStream) -> TokenStream {
};
let chain_exist_entry = quote! {
- id if id == std::any::TypeId::of::<#previous_type>() => true,
+ Self::#previous_type => true,
};
let mut chains = crate::CHAINS.lock().unwrap();
let mut chain_exist = crate::CHAINS_EXIST.lock().unwrap();
+ let mut packed_types = crate::PACKED_TYPES.lock().unwrap();
let chain_entry = chain_entry.to_string();
let chain_exist_entry = chain_exist_entry.to_string();
+ let previous_type_str = previous_type.to_token_stream().to_string();
if !chains.contains(&chain_entry) {
chains.push(chain_entry);
}
- if !chains.contains(&chain_exist_entry) {
+
+ if !chain_exist.contains(&chain_exist_entry) {
chain_exist.push(chain_exist_entry);
}
+ if !packed_types.contains(&previous_type_str) {
+ packed_types.push(previous_type_str);
+ }
+
expanded.into()
}
diff --git a/mingling_macros/src/dispatcher_chain.rs b/mingling_macros/src/dispatcher_chain.rs
index 57c11a3..dc02c33 100644
--- a/mingling_macros/src/dispatcher_chain.rs
+++ b/mingling_macros/src/dispatcher_chain.rs
@@ -8,53 +8,123 @@ use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Ident, Result as SynResult, Token};
-/// Parses input in the format: `"command_name", CommandStruct => ChainStruct`
-struct DispatcherChainInput {
- command_name: syn::LitStr,
- command_struct: Ident,
- pack: Ident,
+enum DispatcherChainInput {
+ Explicit {
+ group_name: Ident,
+ command_name: syn::LitStr,
+ command_struct: Ident,
+ pack: Ident,
+ },
+ Default {
+ command_name: syn::LitStr,
+ command_struct: Ident,
+ pack: Ident,
+ },
}
impl Parse for DispatcherChainInput {
fn parse(input: ParseStream) -> SynResult<Self> {
- let command_name = input.parse()?;
- input.parse::<Token![,]>()?;
- let command_struct = input.parse()?;
- input.parse::<Token![=>]>()?;
- let pack = input.parse()?;
-
- Ok(DispatcherChainInput {
- command_name,
- command_struct,
- pack,
- })
+ let lookahead = input.lookahead1();
+
+ if lookahead.peek(Ident) && input.peek2(Token![,]) && input.peek3(syn::LitStr) {
+ let group_name = input.parse()?;
+ input.parse::<Token![,]>()?;
+ let command_name = input.parse()?;
+ input.parse::<Token![,]>()?;
+ let command_struct = input.parse()?;
+ input.parse::<Token![=>]>()?;
+ let pack = input.parse()?;
+
+ Ok(DispatcherChainInput::Explicit {
+ group_name,
+ command_name,
+ command_struct,
+ pack,
+ })
+ } else if lookahead.peek(syn::LitStr) {
+ // Default format: "command_name", CommandStruct => ChainStruct
+ let command_name = input.parse()?;
+ input.parse::<Token![,]>()?;
+ let command_struct = input.parse()?;
+ input.parse::<Token![=>]>()?;
+ let pack = input.parse()?;
+
+ Ok(DispatcherChainInput::Default {
+ command_name,
+ command_struct,
+ pack,
+ })
+ } else {
+ Err(lookahead.error())
+ }
}
}
pub fn dispatcher_chain(input: TokenStream) -> TokenStream {
- let DispatcherChainInput {
- command_name,
- command_struct,
- pack,
- } = syn::parse_macro_input!(input as DispatcherChainInput);
+ // Parse the input
+ let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput);
- let command_name_str = command_name.value();
-
- let expanded = quote! {
- #[derive(Debug, Default)]
- pub struct #command_struct;
+ // Determine if we're using default or explicit group
+ let (group_name, command_name, command_struct, pack, use_default) = match dispatcher_input {
+ DispatcherChainInput::Explicit {
+ group_name,
+ command_name,
+ command_struct,
+ pack,
+ } => (group_name, command_name, command_struct, pack, false),
+ DispatcherChainInput::Default {
+ command_name,
+ command_struct,
+ pack,
+ } => (
+ Ident::new("DefaultProgram", proc_macro2::Span::call_site()),
+ command_name,
+ command_struct,
+ pack,
+ true,
+ ),
+ };
- ::mingling::macros::pack!(#pack = Vec<String>);
+ let command_name_str = command_name.value();
- impl ::mingling::Dispatcher for #command_struct {
- fn node(&self) -> ::mingling::Node {
- ::mingling::macros::node!(#command_name_str)
+ let expanded = if use_default {
+ // For default case, use DefaultProgram
+ quote! {
+ #[derive(Debug, Default)]
+ pub struct #command_struct;
+
+ ::mingling::macros::pack!(DefaultProgram, #pack = Vec<String>);
+
+ impl ::mingling::Dispatcher<DefaultProgram> for #command_struct {
+ fn node(&self) -> ::mingling::Node {
+ ::mingling::macros::node!(#command_name_str)
+ }
+ fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<DefaultProgram> {
+ #pack::new(args).to_chain()
+ }
+ fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<DefaultProgram>> {
+ Box::new(#command_struct)
+ }
}
- fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess {
- #pack::new(args).to_chain()
- }
- fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher> {
- Box::new(#command_struct)
+ }
+ } else {
+ // For explicit case, use the provided group_name
+ quote! {
+ #[derive(Debug, Default)]
+ pub struct #command_struct;
+
+ ::mingling::macros::pack!(#group_name, #pack = Vec<String>);
+
+ impl ::mingling::Dispatcher<#group_name> for #command_struct {
+ fn node(&self) -> ::mingling::Node {
+ ::mingling::macros::node!(#command_name_str)
+ }
+ fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#group_name> {
+ #pack::new(args).to_chain()
+ }
+ fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#group_name>> {
+ Box::new(#command_struct)
+ }
}
}
};
@@ -63,29 +133,70 @@ pub fn dispatcher_chain(input: TokenStream) -> TokenStream {
}
pub fn dispatcher_render(input: TokenStream) -> TokenStream {
- let DispatcherChainInput {
- command_name,
- command_struct,
- pack,
- } = syn::parse_macro_input!(input as DispatcherChainInput);
+ // Parse the input
+ let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput);
- let command_name_str = command_name.value();
-
- let expanded = quote! {
- #[derive(Debug, Default)]
- pub struct #command_struct;
+ // Determine if we're using default or explicit group
+ let (group_name, command_name, command_struct, pack, use_default) = match dispatcher_input {
+ DispatcherChainInput::Explicit {
+ group_name,
+ command_name,
+ command_struct,
+ pack,
+ } => (group_name, command_name, command_struct, pack, false),
+ DispatcherChainInput::Default {
+ command_name,
+ command_struct,
+ pack,
+ } => (
+ Ident::new("DefaultProgram", proc_macro2::Span::call_site()),
+ command_name,
+ command_struct,
+ pack,
+ true,
+ ),
+ };
- ::mingling::macros::pack!(#pack = Vec<String>);
+ let command_name_str = command_name.value();
- impl ::mingling::Dispatcher for #command_struct {
- fn node(&self) -> ::mingling::Node {
- ::mingling::macros::node!(#command_name_str)
+ let expanded = if use_default {
+ // For default case, use DefaultProgram
+ quote! {
+ #[derive(Debug, Default)]
+ pub struct #command_struct;
+
+ ::mingling::macros::pack!(DefaultProgram, #pack = Vec<String>);
+
+ impl ::mingling::Dispatcher for #command_struct {
+ fn node(&self) -> ::mingling::Node {
+ ::mingling::macros::node!(#command_name_str)
+ }
+ fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess {
+ #pack::new(args).to_render()
+ }
+ fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher> {
+ Box::new(#command_struct)
+ }
}
- fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess {
- #pack::new(args).to_render()
- }
- fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher> {
- Box::new(#command_struct)
+ }
+ } else {
+ // For explicit case, use the provided group_name
+ quote! {
+ #[derive(Debug, Default)]
+ pub struct #command_struct;
+
+ ::mingling::macros::pack!(#group_name, #pack = Vec<String>);
+
+ impl ::mingling::Dispatcher for #command_struct {
+ fn node(&self) -> ::mingling::Node {
+ ::mingling::macros::node!(#command_name_str)
+ }
+ fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess {
+ #pack::new(args).to_render()
+ }
+ fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher> {
+ Box::new(#command_struct)
+ }
}
}
};
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 05bcf0b..832b45a 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -19,6 +19,7 @@ use once_cell::sync::Lazy;
use std::sync::Mutex;
// Global variable declarations for storing chain and renderer mappings
+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()));
pub(crate) static CHAINS_EXIST: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
@@ -55,8 +56,8 @@ pub fn r_println(input: TokenStream) -> TokenStream {
}
#[proc_macro_attribute]
-pub fn chain(_attr: TokenStream, item: TokenStream) -> TokenStream {
- chain::chain_attr(item)
+pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream {
+ chain::chain_attr(attr, item)
}
#[proc_macro_attribute]
@@ -65,14 +66,24 @@ pub fn renderer(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
#[proc_macro]
-pub fn program(input: TokenStream) -> TokenStream {
- let name = parse_macro_input!(input as Ident);
+pub fn gen_program(input: TokenStream) -> TokenStream {
+ let name = if input.is_empty() {
+ Ident::new("DefaultProgram", proc_macro2::Span::call_site())
+ } else {
+ parse_macro_input!(input as Ident)
+ };
+ let packed_types = PACKED_TYPES.lock().unwrap().clone();
let renderers = RENDERERS.lock().unwrap().clone();
let chains = CHAINS.lock().unwrap().clone();
let renderer_exist = RENDERERS_EXIST.lock().unwrap().clone();
let chain_exist = CHAINS_EXIST.lock().unwrap().clone();
+ let packed_types: Vec<proc_macro2::TokenStream> = packed_types
+ .iter()
+ .map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
+ .collect();
+
let renderer_tokens: Vec<proc_macro2::TokenStream> = renderers
.iter()
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
@@ -94,23 +105,50 @@ pub fn program(input: TokenStream) -> TokenStream {
.collect();
let expanded = quote! {
- pub struct #name;
+ ::mingling::macros::pack!(#name, RendererNotFound = String);
+ ::mingling::macros::pack!(#name, DispatcherNotFound = Vec<String>);
+
+ #[derive(Debug, Default, PartialEq, Eq, Clone)]
+ #[repr(u32)]
+ pub enum #name {
+ #[default]
+ __FallBack,
+ DispatcherNotFound,
+ RendererNotFound,
+ #(#packed_types),*
+ }
+
+ impl ::std::fmt::Display for #name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ match self {
+ #name::__FallBack => write!(f, "__FallBack"),
+ #name::DispatcherNotFound => {
+ write!(f, "DispatcherNotFound")
+ }
+ #name::RendererNotFound => {
+ write!(f, "RendererNotFound")
+ }
+ #(#name::#packed_types => write!(f, stringify!(#packed_types)),)*
+ }
+ }
+ }
impl ::mingling::ProgramCollect for #name {
+ type Enum = #name;
::mingling::__dispatch_program_renderers!(
#(#renderer_tokens)*
);
::mingling::__dispatch_program_chains!(
#(#chain_tokens)*
);
- fn has_renderer(any: &::mingling::AnyOutput) -> bool {
- match any.type_id {
+ fn has_renderer(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
+ match any.member_id {
#(#renderer_exist_tokens)*
_ => false
}
}
- fn has_chain(any: &::mingling::AnyOutput) -> bool {
- match any.type_id {
+ fn has_chain(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
+ match any.member_id {
#(#chain_exist_tokens)*
_ => false
}
@@ -118,7 +156,7 @@ pub fn program(input: TokenStream) -> TokenStream {
}
impl #name {
- pub fn new() -> ::mingling::Program<#name> {
+ pub fn new() -> ::mingling::Program<#name, #name> {
::mingling::Program::new()
}
}
diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs
index bff67d4..c6a6c67 100644
--- a/mingling_macros/src/pack.rs
+++ b/mingling_macros/src/pack.rs
@@ -8,37 +8,80 @@ use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Ident, Result as SynResult, Token, Type};
-/// Parses input in the format: `TypeName = InnerType`
-struct PackInput {
- type_name: Ident,
- inner_type: Type,
+enum PackInput {
+ Explicit {
+ group_name: Ident,
+ type_name: Ident,
+ inner_type: Type,
+ },
+ Default {
+ type_name: Ident,
+ inner_type: Type,
+ },
}
impl Parse for PackInput {
fn parse(input: ParseStream) -> SynResult<Self> {
- let type_name = input.parse()?;
- input.parse::<Token![=]>()?;
- let inner_type = input.parse()?;
+ // Try to parse as explicit format first: GroupName, TypeName = InnerType
+ let lookahead = input.lookahead1();
- Ok(PackInput {
- type_name,
- inner_type,
- })
+ if lookahead.peek(Ident) && input.peek2(Token![,]) {
+ // Explicit format: GroupName, TypeName = InnerType
+ let group_name = input.parse()?;
+ input.parse::<Token![,]>()?;
+ let type_name = input.parse()?;
+ input.parse::<Token![=]>()?;
+ let inner_type = input.parse()?;
+
+ Ok(PackInput::Explicit {
+ group_name,
+ type_name,
+ inner_type,
+ })
+ } else if lookahead.peek(Ident) && input.peek2(Token![=]) {
+ // Default format: TypeName = InnerType
+ let type_name = input.parse()?;
+ input.parse::<Token![=]>()?;
+ let inner_type = input.parse()?;
+
+ Ok(PackInput::Default {
+ type_name,
+ inner_type,
+ })
+ } else {
+ Err(lookahead.error())
+ }
}
}
pub fn pack(input: TokenStream) -> TokenStream {
- let PackInput {
- type_name,
- inner_type,
- } = syn::parse_macro_input!(input as PackInput);
+ // Parse the input
+ let pack_input = syn::parse_macro_input!(input as PackInput);
+
+ // Determine if we're using default or explicit group
+ let (group_name, type_name, inner_type, use_default) = match pack_input {
+ PackInput::Explicit {
+ group_name,
+ type_name,
+ inner_type,
+ } => (group_name, type_name, inner_type, false),
+ PackInput::Default {
+ type_name,
+ inner_type,
+ } => (
+ Ident::new("DefaultProgram", proc_macro2::Span::call_site()),
+ type_name,
+ inner_type,
+ true,
+ ),
+ };
// Generate the struct definition
#[cfg(not(feature = "serde"))]
let struct_def = quote! {
#[derive(Debug)]
pub struct #type_name {
- inner: #inner_type,
+ pub(crate) inner: #inner_type,
}
};
@@ -46,7 +89,7 @@ pub fn pack(input: TokenStream) -> TokenStream {
let struct_def = quote! {
#[derive(Debug, serde::Serialize)]
pub struct #type_name {
- inner: #inner_type,
+ pub(crate) inner: #inner_type,
}
};
@@ -120,42 +163,95 @@ pub fn pack(input: TokenStream) -> TokenStream {
};
let any_out_impl = quote! {
- impl Into<mingling::AnyOutput> for #type_name {
- fn into(self) -> mingling::AnyOutput {
+ impl Into<mingling::AnyOutput<#group_name>> for #type_name {
+ fn into(self) -> mingling::AnyOutput<#group_name> {
mingling::AnyOutput::new(self)
}
}
- impl Into<mingling::ChainProcess> for #type_name {
- fn into(self) -> mingling::ChainProcess {
+ impl Into<mingling::ChainProcess<#group_name>> for #type_name {
+ fn into(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_chain()
}
}
impl #type_name {
/// Converts the wrapper type into a `ChainProcess` for chaining operations.
- pub fn to_chain(self) -> mingling::ChainProcess {
+ pub fn to_chain(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_chain()
}
/// Converts the wrapper type into a `ChainProcess` for rendering operations.
- pub fn to_render(self) -> mingling::ChainProcess {
+ pub fn to_render(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_renderer()
}
}
};
+ let group_impl = quote! {
+ impl ::mingling::Groupped<#group_name> for #type_name {
+ fn member_id() -> #group_name {
+ #group_name::#type_name
+ }
+ }
+ };
+
// Combine all implementations
- let expanded = quote! {
- #struct_def
+ let expanded = if use_default {
+ // For default case, use DefaultProgram
+ quote! {
+ #struct_def
+
+ #new_impl
+ #from_into_impl
+ #as_ref_impl
+ #deref_impl
+ #default_impl
+
+ impl Into<mingling::AnyOutput<DefaultProgram>> for #type_name {
+ fn into(self) -> mingling::AnyOutput<DefaultProgram> {
+ mingling::AnyOutput::new(self)
+ }
+ }
+
+ impl From<#type_name> for mingling::ChainProcess<DefaultProgram> {
+ fn from(value: #type_name) -> Self {
+ mingling::AnyOutput::new(value).route_chain()
+ }
+ }
- #new_impl
- #from_into_impl
- #as_ref_impl
- #deref_impl
- #default_impl
+ impl #type_name {
+ /// Converts the wrapper type into a `ChainProcess` for chaining operations.
+ pub fn to_chain(self) -> mingling::ChainProcess<DefaultProgram> {
+ mingling::AnyOutput::new(self).route_chain()
+ }
- #any_out_impl
+ /// Converts the wrapper type into a `ChainProcess` for rendering operations.
+ pub fn to_render(self) -> mingling::ChainProcess<DefaultProgram> {
+ mingling::AnyOutput::new(self).route_renderer()
+ }
+ }
+
+ impl ::mingling::Groupped<DefaultProgram> for #type_name {
+ fn member_id() -> DefaultProgram {
+ DefaultProgram::#type_name
+ }
+ }
+ }
+ } else {
+ // For explicit case, use the provided group_name
+ quote! {
+ #struct_def
+
+ #new_impl
+ #from_into_impl
+ #as_ref_impl
+ #deref_impl
+ #default_impl
+
+ #any_out_impl
+ #group_impl
+ }
};
expanded.into()
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index 21c20c8..0e32b40 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -4,7 +4,7 @@
//! generating structs that implement the `Renderer` trait from functions.
use proc_macro::TokenStream;
-use quote::quote;
+use quote::{ToTokens, quote};
use syn::spanned::Spanned;
use syn::{FnArg, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input};
@@ -106,22 +106,29 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream {
};
let renderer_exist_entry = quote! {
- id if id == std::any::TypeId::of::<#previous_type>() => true,
+ Self::#previous_type => true,
};
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();
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();
if !renderers.contains(&renderer_entry_str) {
renderers.push(renderer_entry_str);
}
+
if !renderer_exist.contains(&renderer_exist_entry_str) {
renderer_exist.push(renderer_exist_entry_str);
}
+ if !packed_types.contains(&previous_type_str) {
+ packed_types.push(previous_type_str);
+ }
+
// Generate the struct and implementation
// We need to create a wrapper function that adds the r parameter
let expanded = quote! {