aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md18
-rw-r--r--mingling/src/lib.rs2
-rw-r--r--mingling_core/src/any.rs88
-rw-r--r--mingling_core/src/asset/chain.rs7
-rw-r--r--mingling_core/src/asset/chain/error.rs33
-rw-r--r--mingling_core/src/asset/dispatcher.rs5
-rw-r--r--mingling_core/src/program.rs65
-rw-r--r--mingling_core/src/program/exec.rs76
-rw-r--r--mingling_core/src/program/setup.rs4
-rw-r--r--mingling_core/src/program/string_vec.rs56
-rw-r--r--mingling_macros/src/dispatcher_chain.rs78
-rw-r--r--mingling_macros/src/lib.rs5
12 files changed, 187 insertions, 250 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1cc9b11..c5f70af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelogs
+### Release 0.1.7
+
+#### Fixes:
+
+None
+
+#### Features:
+
+1. Added function `new_with_args` to `Program`
+2. Added function `dispatch_args_dynamic` to `Program`
+
+#### **BREAKING CHANGES**:
+
+1. Removed macro `dispatcher_render!` from `mingling_macros`
+2. The `<..., Group>` in `Program<Collect, Group>` no longer requires `std::fmt::Display`
+
+---
+
### Release 0.1.6
`Mingling` 0.1.6 primarily focuses on optimizing the writing experience and code completion.
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 91bce8d..d581e83 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -79,8 +79,6 @@ pub mod macros {
pub use mingling_macros::completion;
/// Used to create a dispatcher that routes to a `Chain`
pub use mingling_macros::dispatcher;
- /// Used to create a dispatcher that routes to a `Renderer`
- pub use mingling_macros::dispatcher_render;
/// Used to collect data and create a command-line context
pub use mingling_macros::gen_program;
/// Used to create a `Node` struct via a literal
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index 57eddfb..b077fba 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -1,5 +1,3 @@
-use std::fmt::Display;
-
#[cfg(feature = "general_renderer")]
use serde::Serialize;
@@ -19,19 +17,13 @@ pub mod group;
/// - Under the `general_renderer` feature, the passed value must ensure it implements `serde::Serialize`
/// - It is recommended to use the `pack!` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to create types that can be converted to `AnyOutput`, which guarantees runtime safety
#[derive(Debug)]
-pub struct AnyOutput<G>
-where
- G: Display,
-{
+pub struct AnyOutput<G> {
pub(crate) inner: Box<dyn std::any::Any + Send + 'static>,
pub type_id: std::any::TypeId,
pub member_id: G,
}
-impl<G> AnyOutput<G>
-where
- G: Display,
-{
+impl<G> AnyOutput<G> {
/// Create an AnyOutput from a `Send + Groupped<G> + Serialize` type
#[cfg(feature = "general_renderer")]
pub fn new<T>(value: T) -> Self
@@ -96,10 +88,7 @@ where
}
}
-impl<G> std::ops::Deref for AnyOutput<G>
-where
- G: Display,
-{
+impl<G> std::ops::Deref for AnyOutput<G> {
type Target = dyn std::any::Any + Send + 'static;
fn deref(&self) -> &Self::Target {
@@ -107,10 +96,7 @@ where
}
}
-impl<G> std::ops::DerefMut for AnyOutput<G>
-where
- G: Display,
-{
+impl<G> std::ops::DerefMut for AnyOutput<G> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.inner
}
@@ -122,13 +108,7 @@ where
/// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`Next::Chain`](./enum.Next.html)`))` to continue execution with this type next
/// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`Next::Renderer`](./enum.Next.html)`))` to render this type next and output to the terminal
/// - Returns `Err(`[`ChainProcessError`](./error/enum.ChainProcessError.html)`]` to terminate the program directly
-pub enum ChainProcess<G>
-where
- G: Display,
-{
- Ok((AnyOutput<G>, Next)),
- Err(ChainProcessError),
-}
+pub type ChainProcess<G> = Result<(AnyOutput<G>, Next), ChainProcessError>;
/// Indicates the next step after processing
///
@@ -139,60 +119,8 @@ pub enum Next {
Renderer,
}
-impl<G> ChainProcess<G>
-where
- G: Display,
-{
- /// Returns true if the result is Ok (has a next step)
- pub fn is_next(&self) -> bool {
- matches!(self, Self::Ok(_))
- }
-
- /// Returns true if the result is an error
- pub fn is_err(&self) -> bool {
- matches!(self, Self::Err(_))
- }
-
- /// Returns the next step if the result is Ok
- pub fn next(&self) -> Option<&Next> {
- match self {
- Self::Ok((_, next)) => Some(next),
- Self::Err(_) => None,
- }
- }
-
- /// Returns the error if the result is Err
- pub fn err(&self) -> Option<&ChainProcessError> {
- match self {
- Self::Ok(_) => None,
- Self::Err(err) => Some(err),
- }
- }
-
- /// Unwraps the result, panics if it's an error
- pub fn unwrap(self) -> (AnyOutput<G>, Next) {
- match self {
- Self::Ok(tuple) => tuple,
- Self::Err(_) => panic!("called `ChainProcess2::unwrap()` on an `Error` value"),
- }
- }
-
- /// Returns the Ok value or a provided default
- pub fn unwrap_or(self, default: (AnyOutput<G>, Next)) -> (AnyOutput<G>, Next) {
- match self {
- Self::Ok(tuple) => tuple,
- Self::Err(_) => default,
- }
- }
-
- /// Returns the Ok value or computes it from the error
- pub fn unwrap_or_else<F>(self, f: F) -> (AnyOutput<G>, Next)
- where
- F: FnOnce(ChainProcessError) -> (AnyOutput<G>, Next),
- {
- match self {
- Self::Ok(tuple) => tuple,
- Self::Err(err) => f(err),
- }
+impl<G> From<AnyOutput<G>> for ChainProcess<G> {
+ fn from(value: AnyOutput<G>) -> Self {
+ ChainProcess::Ok((value, Next::Chain))
}
}
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs
index c41b716..1b488fe 100644
--- a/mingling_core/src/asset/chain.rs
+++ b/mingling_core/src/asset/chain.rs
@@ -1,15 +1,10 @@
-use std::fmt::Display;
-
use crate::ChainProcess;
#[doc(hidden)]
pub mod error;
/// Takes over a type (G: Previous) and converts it to another [AnyOutput](./struct.AnyOutput.html)
-pub trait Chain<G>
-where
- G: Display,
-{
+pub trait Chain<G> {
/// The previous type in the chain
type Previous;
diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs
index cc22bdc..5221e57 100644
--- a/mingling_core/src/asset/chain/error.rs
+++ b/mingling_core/src/asset/chain/error.rs
@@ -1,3 +1,5 @@
+use crate::error::{ProgramExecuteError, ProgramInternalExecuteError};
+
#[derive(thiserror::Error, Debug)]
pub enum ChainProcessError {
#[error("Other error: {0}")]
@@ -6,3 +8,34 @@ pub enum ChainProcessError {
#[error("IO error: {0}")]
IO(#[from] std::io::Error),
}
+
+impl From<ProgramExecuteError> for ChainProcessError {
+ fn from(value: ProgramExecuteError) -> Self {
+ match value {
+ ProgramExecuteError::DispatcherNotFound => {
+ ChainProcessError::Other("DispatcherNotFound".into())
+ }
+ ProgramExecuteError::RendererNotFound(r) => {
+ ChainProcessError::Other(format!("RendererNotFound: {}", r))
+ }
+ ProgramExecuteError::Other(e) => ChainProcessError::Other(e),
+ }
+ }
+}
+
+impl From<ProgramInternalExecuteError> for ChainProcessError {
+ fn from(value: ProgramInternalExecuteError) -> Self {
+ match value {
+ ProgramInternalExecuteError::DispatcherNotFound => {
+ ChainProcessError::Other("DispatcherNotFound".into())
+ }
+ ProgramInternalExecuteError::RendererNotFound(r) => {
+ ChainProcessError::Other(format!("RendererNotFound: {}", r))
+ }
+ ProgramInternalExecuteError::Other(e) => ChainProcessError::Other(e),
+ ProgramInternalExecuteError::IO(e) => {
+ ChainProcessError::Other(format!("IOError: {:?}", e))
+ }
+ }
+ }
+}
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index 0f4675c..72b31e9 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -6,10 +6,7 @@ use crate::{ChainProcess, Program, asset::node::Node};
///
/// Note: If you are using [mingling_macros](https://crates.io/crates/mingling_macros),
/// you can use the `dispatcher!("node.subnode", CommandType => Entry)` macro to declare a `Dispatcher`
-pub trait Dispatcher<G>
-where
- G: Display,
-{
+pub trait Dispatcher<G> {
/// Returns a command node for matching user input
fn node(&self) -> Node;
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index db5957b..d655c74 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -8,8 +8,9 @@ use crate::error::GeneralRendererSerializeError;
use std::env;
use crate::{
- AnyOutput, ChainProcess, RenderResult, asset::dispatcher::Dispatcher,
- error::ProgramExecuteError,
+ AnyOutput, ChainProcess, RenderResult,
+ asset::dispatcher::Dispatcher,
+ error::{ChainProcessError, ProgramExecuteError},
};
use std::{fmt::Display, sync::OnceLock};
@@ -27,13 +28,16 @@ pub use config::*;
mod flag;
pub use flag::*;
+mod string_vec;
+pub use string_vec::*;
+
/// Global static reference to the current program instance
static THIS_PROGRAM: OnceLock<Option<Box<dyn std::any::Any + Send + Sync>>> = OnceLock::new();
/// Returns a reference to the current program instance, panics if not set.
pub fn this<C>() -> &'static Program<C, C>
where
- C: ProgramCollect + Display + 'static,
+ C: ProgramCollect + 'static,
{
try_get_this_program().expect("Program not initialized")
}
@@ -41,7 +45,7 @@ where
/// Returns a reference to the current program instance, if set.
fn try_get_this_program<C>() -> Option<&'static Program<C, C>>
where
- C: ProgramCollect + Display + 'static,
+ C: ProgramCollect + 'static,
{
THIS_PROGRAM
.get()?
@@ -54,7 +58,6 @@ where
pub struct Program<C, G>
where
C: ProgramCollect,
- G: Display,
{
pub(crate) collect: std::marker::PhantomData<C>,
pub(crate) group: std::marker::PhantomData<G>,
@@ -72,26 +75,31 @@ where
impl<C, G> Program<C, G>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
- /// Creates a new Program instance, initializing args from environment.
+ /// Creates a new Program instance, initializing command-line arguments from the environment.
pub fn new() -> Self {
+ #[cfg(not(windows))]
+ return Self::new_with_args(env::args().collect::<Vec<String>>());
+
+ #[cfg(windows)]
+ return Self::new_with_args({
+ std::env::args_os()
+ .map(|arg| {
+ use std::os::windows::ffi::OsStrExt;
+
+ let wide: Vec<u16> = arg.encode_wide().collect();
+ String::from_utf16_lossy(&wide)
+ })
+ .collect()
+ });
+ }
+
+ /// Creates a new Program instance with the provided command-line arguments.
+ pub fn new_with_args(args: impl Into<StringVec>) -> Self {
Program {
collect: std::marker::PhantomData,
group: std::marker::PhantomData,
- #[cfg(not(windows))]
- args: env::args().collect(),
- #[cfg(windows)]
- args: {
- std::env::args_os()
- .map(|arg| {
- use std::os::windows::ffi::OsStrExt;
-
- let wide: Vec<u16> = arg.encode_wide().collect();
- String::from_utf16_lossy(&wide)
- })
- .collect()
- },
+ args: args.into().into(),
dispatcher: Vec::new(),
stdout_setting: Default::default(),
user_context: Default::default(),
@@ -116,10 +124,21 @@ where
.unwrap()
}
- // Get all registered dispatcher names from the program
+ /// Get all registered dispatcher names from the program
pub fn get_nodes(&self) -> Vec<(String, &(dyn Dispatcher<G> + Send + Sync))> {
get_nodes(self)
}
+
+ /// Dynamically dispatch input arguments to registered entry types
+ pub fn dispatch_args_dynamic(
+ &self,
+ args: impl Into<StringVec>,
+ ) -> Result<AnyOutput<G>, ChainProcessError> {
+ match exec::dispatch_args_dynamic(self, args.into().into()) {
+ Ok(ok) => Ok(ok),
+ Err(e) => Err(e.into()),
+ }
+ }
}
// Async program
@@ -127,7 +146,6 @@ where
impl<C, G> Program<C, G>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
/// Sets the current program instance and runs the provided async function.
async fn set_instance_and_run<F, Fut>(self, f: F) -> Fut::Output
@@ -201,7 +219,6 @@ where
impl<C, G> Program<C, G>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
/// Sets the current program instance and runs the provided function.
fn set_instance_and_run<F, R>(self, f: F) -> R
@@ -386,7 +403,7 @@ macro_rules! __dispatch_program_chains {
}
/// Get all registered dispatcher names from the program
-pub fn get_nodes<C: ProgramCollect<Enum = G>, G: Display>(
+pub fn get_nodes<C: ProgramCollect<Enum = G>, G>(
program: &Program<C, G>,
) -> Vec<(String, &(dyn Dispatcher<G> + Send + Sync))> {
program
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 8ab2036..68a694e 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -1,7 +1,5 @@
#![allow(clippy::borrowed_box)]
-use std::fmt::Display;
-
use crate::{
AnyOutput, ChainProcess, Dispatcher, Next, Program, ProgramCollect, RenderResult,
error::ProgramInternalExecuteError,
@@ -16,30 +14,10 @@ pub async fn exec<C, G>(
) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
- let mut current;
+ let mut current = dispatch_args_dynamic(program, program.args.clone())?;
let mut stop_next = false;
- // Match user input
- match match_user_input(program, program.args.clone()) {
- Ok((dispatcher, args)) => {
- // Entry point
- current = match dispatcher.begin(args) {
- 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.clone());
- }
- Err(e) => return Err(e),
- };
-
loop {
let final_exec = stop_next;
@@ -76,30 +54,10 @@ where
pub fn exec<C, G>(program: &Program<C, G>) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
- let mut current;
+ let mut current = dispatch_args_dynamic(program, program.args.clone())?;
let mut stop_next = false;
- // Match user input
- match match_user_input(program, program.args.clone()) {
- Ok((dispatcher, args)) => {
- // Entry point
- current = match dispatcher.begin(args) {
- 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.clone());
- }
- Err(e) => return Err(e),
- };
-
loop {
let final_exec = stop_next;
@@ -132,15 +90,39 @@ where
Ok(RenderResult::default())
}
+/// Dynamically dispatch input arguments to registered entry types
+pub(crate) fn dispatch_args_dynamic<C, G>(
+ program: &Program<C, G>,
+ args: Vec<String>,
+) -> Result<AnyOutput<G>, ProgramInternalExecuteError>
+where
+ C: ProgramCollect<Enum = G>,
+{
+ let next = match match_user_input(program, args) {
+ Ok((dispatcher, args)) => {
+ // Entry point
+ match dispatcher.begin(args) {
+ ChainProcess::Ok((any, _)) => any,
+ ChainProcess::Err(e) => return Err(e.into()),
+ }
+ }
+ Err(ProgramInternalExecuteError::DispatcherNotFound) => {
+ // No matching Dispatcher is found
+ C::build_dispatcher_not_found(program.args.clone())
+ }
+ Err(e) => return Err(e),
+ };
+ Ok(next)
+}
+
/// Match user input against registered dispatchers and return the matched dispatcher and remaining arguments.
#[allow(clippy::type_complexity)]
-pub fn match_user_input<C, G>(
+pub(crate) fn match_user_input<C, G>(
program: &Program<C, G>,
args: Vec<String>,
) -> Result<(&(dyn Dispatcher<G> + Send + Sync), Vec<String>), ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = G>,
- G: Display,
{
let nodes = program.get_nodes();
let command = format!("{} ", args.join(" "));
@@ -180,7 +162,7 @@ where
#[inline(always)]
#[allow(unused_variables)]
-fn render<C: ProgramCollect<Enum = G>, G: Display>(
+fn render<C: ProgramCollect<Enum = G>, G>(
program: &Program<C, G>,
any: AnyOutput<G>,
) -> RenderResult {
diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs
index 7b534f3..f095ed3 100644
--- a/mingling_core/src/program/setup.rs
+++ b/mingling_core/src/program/setup.rs
@@ -1,5 +1,3 @@
-use std::fmt::Display;
-
use crate::{ProgramCollect, program::Program};
mod basic;
@@ -13,7 +11,6 @@ pub use general_renderer::*;
pub trait ProgramSetup<C, G>
where
C: ProgramCollect,
- G: Display,
{
fn setup(&mut self, program: &mut Program<C, G>);
}
@@ -21,7 +18,6 @@ where
impl<C, G> Program<C, G>
where
C: ProgramCollect,
- G: Display,
{
/// Load and execute init logic
pub fn with_setup<S: ProgramSetup<C, G> + 'static>(&mut self, mut setup: S) -> S {
diff --git a/mingling_core/src/program/string_vec.rs b/mingling_core/src/program/string_vec.rs
new file mode 100644
index 0000000..478ad74
--- /dev/null
+++ b/mingling_core/src/program/string_vec.rs
@@ -0,0 +1,56 @@
+#[derive(Debug, Clone)]
+pub struct StringVec {
+ vec: Vec<String>,
+}
+
+impl std::ops::Deref for StringVec {
+ type Target = Vec<String>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.vec
+ }
+}
+
+impl From<StringVec> for Vec<String> {
+ fn from(val: StringVec) -> Self {
+ val.vec
+ }
+}
+
+impl<const N: usize> From<[&str; N]> for StringVec {
+ fn from(slice: [&str; N]) -> Self {
+ StringVec {
+ vec: slice.iter().map(|&s| s.to_string()).collect(),
+ }
+ }
+}
+
+impl From<&[&str]> for StringVec {
+ fn from(slice: &[&str]) -> Self {
+ StringVec {
+ vec: slice.iter().map(|&s| s.to_string()).collect(),
+ }
+ }
+}
+
+impl From<Vec<String>> for StringVec {
+ fn from(vec: Vec<String>) -> Self {
+ StringVec { vec }
+ }
+}
+
+impl From<&[String]> for StringVec {
+ fn from(slice: &[String]) -> Self {
+ StringVec {
+ vec: slice.to_vec(),
+ }
+ }
+}
+
+impl From<Vec<&str>> for StringVec {
+ fn from(vec: Vec<&str>) -> Self {
+ StringVec {
+ vec: vec.iter().map(|&s| s.to_string()).collect(),
+ }
+ }
+}
diff --git a/mingling_macros/src/dispatcher_chain.rs b/mingling_macros/src/dispatcher_chain.rs
index 4038600..6223a81 100644
--- a/mingling_macros/src/dispatcher_chain.rs
+++ b/mingling_macros/src/dispatcher_chain.rs
@@ -145,84 +145,6 @@ pub fn dispatcher_chain(input: TokenStream) -> TokenStream {
expanded.into()
}
-pub fn dispatcher_render(input: TokenStream) -> TokenStream {
- // Parse the input
- let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput);
-
- // 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("ThisProgram", proc_macro2::Span::call_site()),
- command_name,
- command_struct,
- pack,
- true,
- ),
- };
-
- let command_name_str = command_name.value();
-
- let comp_entry = get_comp_entry(&pack);
-
- let expanded = if use_default {
- // For default case, use ThisProgram
- quote! {
- #[derive(Debug, Default)]
- pub struct #command_struct;
-
- ::mingling::macros::pack!(ThisProgram, #pack = Vec<String>);
-
- #comp_entry
-
- 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)
- }
- }
- }
- } 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>);
-
- #comp_entry
-
- 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)
- }
- }
- }
- };
-
- expanded.into()
-}
-
#[cfg(feature = "comp")]
fn get_comp_entry(entry_name: &Ident) -> proc_macro2::TokenStream {
let comp_entry = quote! {
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 8df8ad7..a1493ba 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -59,11 +59,6 @@ pub fn dispatcher(input: TokenStream) -> TokenStream {
}
#[proc_macro]
-pub fn dispatcher_render(input: TokenStream) -> TokenStream {
- dispatcher_chain::dispatcher_render(input)
-}
-
-#[proc_macro]
pub fn r_print(input: TokenStream) -> TokenStream {
render::r_print(input)
}