From ab7c5785fb290541ad4361c0d46241817c3ff5f9 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 18 May 2026 16:02:57 +0800 Subject: Refactor program module into submodules --- mingling_core/src/program.rs | 338 ++----------------------------------------- 1 file changed, 15 insertions(+), 323 deletions(-) (limited to 'mingling_core/src/program.rs') diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs index 7c667e4..0a81cc7 100644 --- a/mingling_core/src/program.rs +++ b/mingling_core/src/program.rs @@ -1,28 +1,15 @@ -use crate::error::ProgramPanic; - -#[cfg(feature = "comp")] -use crate::{ShellContext, Suggest}; - -#[cfg(feature = "general_renderer")] -use crate::error::GeneralRendererSerializeError; - #[cfg(not(windows))] use std::env; use crate::{ - AnyOutput, ChainProcess, GlobalResources, Groupped, RenderResult, - asset::dispatcher::Dispatcher, - error::{ChainProcessError, ProgramExecuteError}, + AnyOutput, GlobalResources, asset::dispatcher::Dispatcher, error::ChainProcessError, hook::ProgramHook, }; use std::{ collections::HashMap, - sync::{Arc, Mutex, OnceLock}, + sync::{Arc, Mutex}, }; -#[cfg(feature = "async")] -use std::pin::Pin; - #[doc(hidden)] pub mod error; #[doc(hidden)] @@ -32,6 +19,19 @@ pub mod hook; #[doc(hidden)] pub mod setup; +mod collection; +pub use collection::*; + +mod once_exec; + +#[cfg(feature = "repl")] +mod repl_exec; +#[cfg(feature = "repl")] +pub use repl_exec::*; + +mod single_instance; +pub use single_instance::*; + mod config; pub use config::*; @@ -41,25 +41,6 @@ pub use flag::*; mod string_vec; pub use string_vec::*; -/// Global static reference to the current program instance -static THIS_PROGRAM: OnceLock>> = OnceLock::new(); - -/// Returns a reference to the current program instance, panics if not set. -pub fn this() -> &'static Program -where - C: ProgramCollect + 'static, -{ - try_get_this_program().expect("Program not initialized") -} - -/// Returns a reference to the current program instance, if set. -fn try_get_this_program() -> Option<&'static Program> -where - C: ProgramCollect + 'static, -{ - THIS_PROGRAM.get()?.as_ref()?.downcast_ref::>() -} - /// Program, used to define the behavior of the entire command-line program #[derive(Default)] pub struct Program @@ -173,295 +154,6 @@ where } } -// Async program -#[cfg(feature = "async")] -impl Program -where - C: ProgramCollect, -{ - async fn exec_wrapper(self, f: F) -> Result - where - C: 'static + Send + Sync, - F: FnOnce(&'static Program) -> Fut + Send + Sync, - Fut: Future + Send, - { - THIS_PROGRAM.get_or_init(|| Some(Box::new(self))); - let program = THIS_PROGRAM - .get() - .unwrap() - .as_ref() - .unwrap() - .downcast_ref::>() - .unwrap(); - - #[cfg(not(panic = "abort"))] - if program.stdout_setting.silence_panic { - std::panic::set_hook(Box::new(|_| {})); - } - - #[cfg(panic = "abort")] - return Ok(f(program)); - - #[cfg(not(panic = "abort"))] - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(program))) { - Ok(fut) => Ok(fut.await), - Err(panic_info) => { - let panic_payload = ProgramPanic { - payload: panic_info, - }; - program.run_hook_exec_panic(&panic_payload); - Err(panic_payload) - } - } - } - - /// Run the command line program - pub async fn exec_without_render(mut self) -> Result - where - C: 'static + Send + Sync, - { - // Run hooks - self.run_hook_on_begin(); - - self.args = self.args.iter().skip(1).cloned().collect(); - match self - .exec_wrapper(|p| async { crate::exec::exec(p).await.map_err(|e| e.into()) }) - .await - { - Ok(r) => r, - Err(e) => Err(ProgramExecuteError::Panic(e)), - } - } - - /// Run the command line program - pub async fn exec(self) -> i32 - where - C: 'static + Send + Sync, - { - let stdout_setting = self.stdout_setting.clone(); - let result = match self.exec_without_render().await { - Ok(r) => r, - Err(e) => match e { - ProgramExecuteError::DispatcherNotFound => { - eprintln!("Dispatcher not found"); - return 1; - } - ProgramExecuteError::RendererNotFound(renderer_name) => { - eprintln!("Renderer `{}` not found", renderer_name); - return 1; - } - ProgramExecuteError::Other(e) => { - eprintln!("{}", e); - return 1; - } - ProgramExecuteError::Panic(unwinded_error) => { - eprintln!("{}", unwinded_error); - return 1; - } - }, - }; - - // Render result - if stdout_setting.render_output && !result.is_empty() { - let exit_code = result.exit_code; - print!("{}", result); - - if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) - && stdout_setting.error_output - { - eprintln!("{}", e); - 1 - } else { - exit_code - } - } else { - 0 - } - } - - /// Run the command line program, then exit - pub async fn exec_and_exit(self) - where - C: 'static + Send + Sync, - { - std::process::exit(self.exec().await) - } -} - -// Sync program -#[cfg(not(feature = "async"))] -impl Program -where - C: ProgramCollect, -{ - fn exec_wrapper(self, f: F) -> Result - where - C: 'static + Send + Sync, - F: FnOnce(&'static Program) -> R + Send + Sync, - { - THIS_PROGRAM.get_or_init(|| Some(Box::new(self))); - let program = THIS_PROGRAM - .get() - .unwrap() - .as_ref() - .unwrap() - .downcast_ref::>() - .unwrap(); - - #[cfg(not(panic = "abort"))] - if program.stdout_setting.silence_panic { - std::panic::set_hook(Box::new(|_| {})); - } - - #[cfg(panic = "abort")] - return Ok(f(program)); - - #[cfg(not(panic = "abort"))] - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(program))) { - Ok(result) => Ok(result), - Err(panic_info) => { - let panic_payload = ProgramPanic { - payload: panic_info, - }; - program.run_hook_exec_panic(&panic_payload); - Err(panic_payload) - } - } - } - - /// Run the command line program - pub fn exec_without_render(mut self) -> Result - where - C: 'static + Send + Sync, - { - // Run hooks - self.run_hook_on_begin(); - - self.args = self.args.iter().skip(1).cloned().collect(); - match self.exec_wrapper(|p| crate::exec::exec(p).map_err(|e| e.into())) { - Ok(r) => r, - Err(e) => Err(ProgramExecuteError::Panic(e)), - } - } - - /// Run the command line program - pub fn exec(self) -> i32 - where - C: '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 1; - } - ProgramExecuteError::RendererNotFound(renderer_name) => { - eprintln!("Renderer `{}` not found", renderer_name); - return 1; - } - ProgramExecuteError::Other(e) => { - eprintln!("{}", e); - return 1; - } - ProgramExecuteError::Panic(unwinded_error) => { - eprintln!("{}", unwinded_error); - return 1; - } - }, - }; - - // Render result - if stdout_setting.render_output && !result.is_empty() { - let exit_code = result.exit_code; - print!("{}", result); - - if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) - && stdout_setting.error_output - { - eprintln!("{}", e); - 1 - } else { - exit_code - } - } else { - 0 - } - } - - /// Run the command line program, then exit - pub fn exec_and_exit(self) - where - C: 'static + Send + Sync, - { - std::process::exit(self.exec()) - } -} - -/// Collected program context -/// -/// Note: It is recommended to use the `gen_program!()` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to automatically create this type -pub trait ProgramCollect { - /// Enum type representing internal IDs for the program - type Enum; - type DispatcherNotFound: Groupped; - type RendererNotFound: Groupped; - type EmptyResult: Groupped; - - /// Use a prefix tree to quickly match arguments and dispatch to an Entry - #[cfg(feature = "dispatch_tree")] - fn dispatch_args_trie( - raw: &Vec, - ) -> Result, crate::error::ProgramInternalExecuteError>; - - /// Get all registered dispatcher names from the program - #[cfg(feature = "dispatch_tree")] - fn get_nodes() -> Vec<(String, &'static (dyn Dispatcher + Send + Sync))>; - - /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a renderer was not found - fn build_renderer_not_found(member_id: Self::Enum) -> AnyOutput; - - /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a dispatcher was not found - fn build_dispatcher_not_found(args: Vec) -> AnyOutput; - - /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that the chain returned an empty result - fn build_empty_result() -> AnyOutput; - - /// Render the input [AnyOutput](./struct.AnyOutput.html) - fn render(any: AnyOutput, r: &mut RenderResult); - - /// Render help for Entry - fn render_help(any: AnyOutput, 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, - ) -> Pin> + 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) -> ChainProcess; - - /// Match and execute specific completion logic based on any Entry - #[cfg(feature = "comp")] - fn do_comp(any: &AnyOutput, ctx: &ShellContext) -> Suggest; - - /// Whether the program has a renderer that can handle the current [AnyOutput](./struct.AnyOutput.html) - fn has_renderer(any: &AnyOutput) -> bool; - - /// Whether the program has a chain that can handle the current [AnyOutput](./struct.AnyOutput.html) - fn has_chain(any: &AnyOutput) -> bool; - - /// Perform general rendering and presentation of any type - #[cfg(feature = "general_renderer")] - fn general_render( - any: AnyOutput, - setting: &GeneralRendererSetting, - ) -> Result; -} - #[macro_export] #[doc(hidden)] macro_rules! __dispatch_program_renderers { -- cgit