diff options
| -rw-r--r-- | mingling_core/src/program/once_exec.rs | 194 | ||||
| -rw-r--r-- | mingling_core/src/program/repl_exec.rs | 126 |
2 files changed, 109 insertions, 211 deletions
diff --git a/mingling_core/src/program/once_exec.rs b/mingling_core/src/program/once_exec.rs index a846d04..96723fd 100644 --- a/mingling_core/src/program/once_exec.rs +++ b/mingling_core/src/program/once_exec.rs @@ -1,28 +1,10 @@ use crate::THIS_PROGRAM; use crate::{Program, ProgramCollect, RenderResult, error::ProgramExecuteError}; -// Async program -#[cfg(feature = "async")] impl<C> Program<C> where C: ProgramCollect<Enum = C>, { - pub(crate) async fn exec_wrapper<F, Fut>(self, f: F) -> Fut::Output - where - C: 'static + Send + Sync, - F: FnOnce(&'static Program<C>) -> Fut + Send + Sync, - Fut: Future + Send, - { - THIS_PROGRAM.set(Box::new(self)); - let program = THIS_PROGRAM - .get_raw() - .unwrap() - .downcast_ref::<Program<C>>() - .unwrap(); - - f(program).await - } - /// Run the command line program /// /// # Errors @@ -33,7 +15,8 @@ where /// # Panics /// /// Panics if the program encounters a non-recoverable internal error. - pub async fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError> + #[might_be_async::func] + pub fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError> where C: 'static + Send + Sync, { @@ -42,21 +25,56 @@ where self.args = self.args.iter().skip(1).cloned().collect(); - return self - .exec_wrapper(|p| async { crate::exec::exec(p).await.map_err(|e| e.into()) }) - .await; + #[cfg(not(feature = "async"))] + { + #[cfg(panic = "abort")] + return self.exec_wrapper(|p| crate::exec::exec(p).map_err(|e| e.into())); + + #[cfg(not(panic = "abort"))] + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + self.exec_wrapper(|p| crate::exec::exec(p).map_err(std::convert::Into::into)) + })) { + Ok(result) => result, + Err(panic_info) => { + let panic_payload = crate::error::ProgramPanic { + payload: panic_info, + }; + + let program = THIS_PROGRAM + .get_raw() + .unwrap() + .downcast_ref::<Program<C>>() + .unwrap(); + + #[cfg(not(feature = "async"))] + program.run_hook_exec_panic(crate::hook::HookPanicInfo { + panic: &panic_payload, + }); + + Err(ProgramExecuteError::Panic(panic_payload)) + } + } + } + + #[cfg(feature = "async")] + { + return self + .exec_wrapper(|p| async { crate::exec::exec(p).await.map_err(|e| e.into()) }) + .await; + } } /// Run the command line program #[must_use] - pub async fn exec(self) -> i32 + #[might_be_async::func] + pub fn exec(self) -> i32 where C: 'static + Send + Sync, { use crate::error::ProgramExecuteError; let stdout_setting = self.stdout_setting.clone(); - let result = match self.exec_without_render().await { + let result = match might_be_async::invoke!(self.exec_without_render()) { Ok(r) => r, Err(e) => match e { ProgramExecuteError::DispatcherNotFound => { @@ -88,11 +106,12 @@ where } /// Run the command line program, then exit - pub async fn exec_and_exit(self) + #[might_be_async::func] + pub fn exec_and_exit(self) where C: 'static + Send + Sync, { - let exit_code = self.exec().await; + let exit_code = might_be_async::invoke!(self.exec()); // SAFETY: exec() is synchronous — it returns only after all // chain handlers and renderers have finished. No code still // holds references from get_raw() at this point. @@ -101,6 +120,29 @@ where } } +// Async program +#[cfg(feature = "async")] +impl<C> Program<C> +where + C: ProgramCollect<Enum = C>, +{ + pub(crate) async fn exec_wrapper<F, Fut>(self, f: F) -> Fut::Output + where + C: 'static + Send + Sync, + F: FnOnce(&'static Program<C>) -> Fut + Send + Sync, + Fut: Future + Send, + { + THIS_PROGRAM.set(Box::new(self)); + let program = THIS_PROGRAM + .get_raw() + .unwrap() + .downcast_ref::<Program<C>>() + .unwrap(); + + f(program).await + } +} + // Sync program #[cfg(not(feature = "async"))] impl<C> Program<C> @@ -126,104 +168,4 @@ where f(program) } - - /// Run the command line program - /// - /// # Errors - /// - /// Returns `Err(ProgramExecuteError)` if execution fails, - /// e.g., if no dispatcher is found or a chain error occurs. - /// - /// # Panics - /// - /// Panics if the program encounters a non-recoverable internal error. - pub fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError> - where - C: 'static + Send + Sync, - { - // Run hooks - self.run_hook_on_begin(crate::hook::HookBeginInfo {}); - - self.args = self.args.iter().skip(1).cloned().collect(); - - #[cfg(panic = "abort")] - return self.exec_wrapper(|p| crate::exec::exec(p).map_err(|e| e.into())); - - #[cfg(not(panic = "abort"))] - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - self.exec_wrapper(|p| crate::exec::exec(p).map_err(std::convert::Into::into)) - })) { - Ok(result) => result, - Err(panic_info) => { - let panic_payload = crate::error::ProgramPanic { - payload: panic_info, - }; - - let program = THIS_PROGRAM - .get_raw() - .unwrap() - .downcast_ref::<Program<C>>() - .unwrap(); - - program.run_hook_exec_panic(crate::hook::HookPanicInfo { - panic: &panic_payload, - }); - - Err(ProgramExecuteError::Panic(panic_payload)) - } - } - } - - /// Run the command line program - #[must_use] - pub fn exec(self) -> i32 - where - C: 'static + Send + Sync, - { - use crate::error::ProgramExecuteError; - - 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 1; - } - ProgramExecuteError::RendererNotFound(renderer_name) => { - eprintln!("Renderer `{renderer_name}` not found"); - return 1; - } - ProgramExecuteError::Other(e) => { - eprintln!("{e}"); - return 1; - } - ProgramExecuteError::Panic(unwinded_error) => { - eprintln!("{unwinded_error}"); - return 1; - } - }, - }; - - // Read exit code - // Render result - if stdout_setting.render_output { - result.std_print(); - } - - result.exit_code - } - - /// Run the command line program, then exit - pub fn exec_and_exit(self) - where - C: 'static + Send + Sync, - { - let exit_code = self.exec(); - // SAFETY: exec() is synchronous — it returns only after all - // chain handlers and renderers have finished. No code still - // holds references from get_raw() at this point. - drop(unsafe { THIS_PROGRAM.take() }); - std::process::exit(exit_code) - } } diff --git a/mingling_core/src/program/repl_exec.rs b/mingling_core/src/program/repl_exec.rs index cbda9da..f84b291 100644 --- a/mingling_core/src/program/repl_exec.rs +++ b/mingling_core/src/program/repl_exec.rs @@ -13,7 +13,6 @@ use crate::program::repl_exec::splitter::split_input_string; use crate::{Program, ProgramCollect, RenderResult}; use crate::{program::repl_exec::res::ResREPL, this}; -#[cfg(not(feature = "async"))] impl<C> Program<C> where C: ProgramCollect<Enum = C> + Send + Sync + 'static, @@ -22,106 +21,63 @@ where /// /// This method starts an infinite loop that continuously reads user input, parses commands, executes them, /// and displays the execution result or error message. It is suitable for scenarios requiring command-line interaction with the user. + /// + /// **Note:** When the `async` feature is enabled, panic unwinding is not supported. + /// Any panics during command execution will result in an abort rather than being caught and handled gracefully. + #[might_be_async::func] pub fn exec_repl(mut self) { // Inject default REPL resource self.with_resource(ResREPL::default()); self.run_hook_repl_on_begin(crate::hook::HookREPLBeginInfo {}); - self.exec_wrapper(|p| -> () { - loop { - p.run_hook_repl_pre_readline(crate::hook::HookREPLPreReadlineInfo {}); - let mut readline = p - .run_hook_repl_readline(crate::hook::HookREPLReadlineInfo {}) - .unwrap_or_default(); - p.run_hook_repl_post_readline(crate::hook::HookREPLPostReadlineInfo { - line: &mut readline, - }); - - let args = split_input_string(readline.clone()); - - p.run_hook_repl_pre_exec(crate::hook::HookREPLPreExecInfo { args: &args }); - match exec_once(p, args) { - Ok(r) => { - p.run_hook_repl_on_receive_result( - crate::hook::HookREPLOnReceiveResultInfo { result: &r }, - ); - } - Err(ProgramInternalExecuteError::REPLPanic(panic)) => { - p.run_hook_repl_on_panic(crate::hook::HookREPLOnPanicInfo { - panic: &panic, - }); - } - _ => {} - } - p.run_hook_repl_post_exec(crate::hook::HookREPLPostExecInfo {}); - - if this::<C>().res::<ResREPL>().unwrap().exit { - p.run_hook_repl_exit(crate::hook::HookREPLExitInfo {}); - break; - } - - p.run_hook_repl_loop_once(crate::hook::HookREPLLoopOnceInfo {}); - } - }); + might_be_async::select!( + self.exec_wrapper(async |p| -> () { + repl_loop(p).await; + }) + else + self.exec_wrapper(|p| -> () { repl_loop(p); } + ) + ); } } -#[cfg(feature = "async")] -impl<C> Program<C> +#[might_be_async::func] +fn repl_loop<C>(p: &'static Program<C>) where C: ProgramCollect<Enum = C> + Send + Sync + 'static, { - /// Executes the REPL interactive CLI mode. - /// - /// This method starts an infinite loop that continuously reads user input, parses commands, executes them, - /// and displays the execution result or error message. It is suitable for scenarios requiring command-line interaction with the user. - /// - /// **Note:** When the `async` feature is enabled, panic unwinding is not supported. - /// Any panics during command execution will result in an abort rather than being caught and handled gracefully. - pub async fn exec_repl(mut self) { - // Inject default REPL resource - self.with_resource(ResREPL::default()); + loop { + p.run_hook_repl_pre_readline(crate::hook::HookREPLPreReadlineInfo {}); + let mut readline = p + .run_hook_repl_readline(crate::hook::HookREPLReadlineInfo {}) + .unwrap_or_default(); + p.run_hook_repl_post_readline(crate::hook::HookREPLPostReadlineInfo { + line: &mut readline, + }); - self.run_hook_repl_on_begin(crate::hook::HookREPLBeginInfo {}); + let args = split_input_string(readline.clone()); - self.exec_wrapper(async |p| -> () { - loop { - p.run_hook_repl_pre_readline(crate::hook::HookREPLPreReadlineInfo {}); - let mut readline = p - .run_hook_repl_readline(crate::hook::HookREPLReadlineInfo {}) - .unwrap_or_default(); - p.run_hook_repl_post_readline(crate::hook::HookREPLPostReadlineInfo { - line: &mut readline, + p.run_hook_repl_pre_exec(crate::hook::HookREPLPreExecInfo { args: &args }); + match might_be_async::invoke!(exec_once(p, args)) { + Ok(r) => { + p.run_hook_repl_on_receive_result(crate::hook::HookREPLOnReceiveResultInfo { + result: &r, }); - - let args = split_input_string(readline.clone()); - - p.run_hook_repl_pre_exec(crate::hook::HookREPLPreExecInfo { args: &args }); - match exec_once(p, args).await { - Ok(r) => { - p.run_hook_repl_on_receive_result( - crate::hook::HookREPLOnReceiveResultInfo { result: &r }, - ); - } - Err(ProgramInternalExecuteError::REPLPanic(panic)) => { - p.run_hook_repl_on_panic(crate::hook::HookREPLOnPanicInfo { - panic: &panic, - }); - } - _ => {} - } - p.run_hook_repl_post_exec(crate::hook::HookREPLPostExecInfo {}); - - if this::<C>().res::<ResREPL>().unwrap().exit { - p.run_hook_repl_exit(crate::hook::HookREPLExitInfo {}); - break; - } - - p.run_hook_repl_loop_once(crate::hook::HookREPLLoopOnceInfo {}); } - }) - .await; + Err(ProgramInternalExecuteError::REPLPanic(panic)) => { + p.run_hook_repl_on_panic(crate::hook::HookREPLOnPanicInfo { panic: &panic }); + } + _ => {} + } + p.run_hook_repl_post_exec(crate::hook::HookREPLPostExecInfo {}); + + if this::<C>().res::<ResREPL>().unwrap().exit { + p.run_hook_repl_exit(crate::hook::HookREPLExitInfo {}); + break; + } + + p.run_hook_repl_loop_once(crate::hook::HookREPLLoopOnceInfo {}); } } |
