diff options
Diffstat (limited to 'mingling_core/src')
| -rw-r--r-- | mingling_core/src/asset/chain.rs | 4 | ||||
| -rw-r--r-- | mingling_core/src/program.rs | 127 | ||||
| -rw-r--r-- | mingling_core/src/program/exec.rs | 61 |
3 files changed, 185 insertions, 7 deletions
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs index 70eda8c..c41b716 100644 --- a/mingling_core/src/asset/chain.rs +++ b/mingling_core/src/asset/chain.rs @@ -14,5 +14,9 @@ where type Previous; /// Process the previous value and return a future that resolves to a [`ChainProcess<G>`](./enum.ChainProcess.html) + #[cfg(feature = "async")] fn proc(p: Self::Previous) -> impl Future<Output = ChainProcess<G>> + Send; + + #[cfg(not(feature = "async"))] + fn proc(p: Self::Previous) -> ChainProcess<G>; } diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs index 7ea3a38..ce4a5fa 100644 --- a/mingling_core/src/program.rs +++ b/mingling_core/src/program.rs @@ -8,7 +8,10 @@ use crate::{ AnyOutput, ChainProcess, RenderResult, asset::dispatcher::Dispatcher, error::ProgramExecuteError, }; -use std::{fmt::Display, pin::Pin, sync::OnceLock}; +use std::{fmt::Display, sync::OnceLock}; + +#[cfg(feature = "async")] +use std::pin::Pin; #[doc(hidden)] pub mod exec; @@ -20,7 +23,6 @@ pub use config::*; mod flag; pub use flag::*; -use tokio::io::AsyncWriteExt; /// Global static reference to the current program instance static THIS_PROGRAM: OnceLock<Option<Box<dyn std::any::Any + Send + Sync>>> = OnceLock::new(); @@ -97,7 +99,7 @@ where } /// Returns a reference to the current program instance, if set. - pub async fn this_program() -> &'static Program<C, G> + pub fn this_program() -> &'static Program<C, G> where C: 'static, G: 'static, @@ -111,6 +113,19 @@ where .unwrap() } + // Get all registered dispatcher names from the program + pub fn get_nodes(&self) -> Vec<(String, &(dyn Dispatcher<G> + Send + Sync))> { + get_nodes(self) + } +} + +// Async program +#[cfg(feature = "async")] +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 where @@ -169,17 +184,84 @@ where // Render result if stdout_setting.render_output && !result.is_empty() { print!("{}", result); - if let Err(e) = tokio::io::stdout().flush().await + if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) && stdout_setting.error_output { eprintln!("{}", e); } } } +} - // Get all registered dispatcher names from the program - pub fn get_nodes(&self) -> Vec<(String, &(dyn Dispatcher<G> + Send + Sync))> { - get_nodes(self) +// Sync program +#[cfg(not(feature = "async"))] +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 + where + C: 'static + Send + Sync, + G: 'static + Send + Sync, + F: FnOnce(&'static Program<C, G>) -> R + Send + Sync, + { + THIS_PROGRAM.get_or_init(|| Some(Box::new(self))); + let program = THIS_PROGRAM + .get() + .unwrap() + .as_ref() + .unwrap() + .downcast_ref::<Program<C, G>>() + .unwrap(); + f(program) + } + + /// Run the command line program + pub fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError> + where + C: 'static + Send + Sync, + G: 'static + Send + Sync, + { + self.args = self.args.iter().skip(1).cloned().collect(); + self.set_instance_and_run(|p| crate::exec::exec(p).map_err(|e| e.into())) + } + + /// Run the command line program + pub fn exec(self) + where + C: 'static + Send + Sync, + G: 'static + Send + Sync, + { + let stdout_setting = self.stdout_setting.clone(); + let result = match self.exec_without_render() { + Ok(r) => r, + Err(e) => match e { + ProgramExecuteError::DispatcherNotFound => { + eprintln!("Dispatcher not found"); + return; + } + ProgramExecuteError::RendererNotFound(renderer_name) => { + eprintln!("Renderer `{}` not found", renderer_name); + return; + } + ProgramExecuteError::Other(e) => { + eprintln!("{}", e); + return; + } + }, + }; + + // Render result + if stdout_setting.render_output && !result.is_empty() { + print!("{}", result); + if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) + && stdout_setting.error_output + { + eprintln!("{}", e); + } + } } } @@ -200,10 +282,15 @@ pub trait ProgramCollect { fn render(any: AnyOutput<Self::Enum>, r: &mut RenderResult); /// Find a matching chain to continue execution based on the input [AnyOutput](./struct.AnyOutput.html), returning a new [AnyOutput](./struct.AnyOutput.html) + #[cfg(feature = "async")] fn do_chain( any: AnyOutput<Self::Enum>, ) -> Pin<Box<dyn Future<Output = ChainProcess<Self::Enum>> + Send>>; + /// Find a matching chain to continue execution based on the input [AnyOutput](./struct.AnyOutput.html), returning a new [AnyOutput](./struct.AnyOutput.html) + #[cfg(not(feature = "async"))] + fn do_chain(any: AnyOutput<Self::Enum>) -> ChainProcess<Self::Enum>; + /// Match and execute specific completion logic based on any Entry #[cfg(feature = "comp")] fn do_comp(any: &AnyOutput<Self::Enum>, ctx: &ShellContext) -> Suggest; @@ -246,6 +333,7 @@ macro_rules! __dispatch_program_renderers { #[macro_export] #[doc(hidden)] +#[cfg(feature = "async")] macro_rules! __dispatch_program_chains { ( $( $chain_ty:ty => $chain_prev:ident, )* @@ -269,6 +357,31 @@ macro_rules! __dispatch_program_chains { }; } +#[macro_export] +#[doc(hidden)] +#[cfg(not(feature = "async"))] +macro_rules! __dispatch_program_chains { + ( + $( $chain_ty:ty => $chain_prev:ident, )* + ) => { + fn do_chain( + any: mingling::AnyOutput<Self::Enum>, + ) -> mingling::ChainProcess<Self::Enum> { + match any.member_id { + $( + 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() }; + <$chain_ty as mingling::Chain<Self::Enum>>::proc(value) + } + )* + _ => panic!("No chain found for type id: {:?}", any.type_id), + } + } + }; +} + /// Get all registered dispatcher names from the program pub fn get_nodes<C: ProgramCollect<Enum = G>, G: Display>( program: &Program<C, G>, diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs index ff7e92b..8ab2036 100644 --- a/mingling_core/src/program/exec.rs +++ b/mingling_core/src/program/exec.rs @@ -10,6 +10,7 @@ use crate::{ #[doc(hidden)] pub mod error; +#[cfg(feature = "async")] pub async fn exec<C, G>( program: &Program<C, G>, ) -> Result<RenderResult, ProgramInternalExecuteError> @@ -71,6 +72,66 @@ where Ok(RenderResult::default()) } +#[cfg(not(feature = "async"))] +pub fn exec<C, G>(program: &Program<C, G>) -> Result<RenderResult, ProgramInternalExecuteError> +where + C: ProgramCollect<Enum = G>, + G: Display, +{ + let mut current; + 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; + + current = { + // If a chain exists, execute as a chain + if C::has_chain(¤t) { + match C::do_chain(current) { + 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(¤t) { + return Ok(render::<C, G>(program, current)); + } + // No renderer exists + else { + stop_next = true; + C::build_renderer_not_found(current.member_id) + } + }; + + if final_exec && stop_next { + break; + } + } + Ok(RenderResult::default()) +} + /// 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>( |
