From 74b5a80475e2230c0a494beac5ec86a985c2974f Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 21 May 2026 15:12:58 +0800 Subject: Refactor REPL hooks into modular setups and add new hook types --- mingling_core/src/program/hook.rs | 144 +++++++++++++++++++++++++++++++++ mingling_core/src/program/repl_exec.rs | 25 +++--- 2 files changed, 156 insertions(+), 13 deletions(-) (limited to 'mingling_core/src') diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs index 8e231f1..f93b022 100644 --- a/mingling_core/src/program/hook.rs +++ b/mingling_core/src/program/hook.rs @@ -48,10 +48,22 @@ where #[cfg(feature = "repl")] pub repl_pre_readline: Option, + /// Custom REPL line reader (only available with `repl` feature) + #[cfg(feature = "repl")] + pub repl_readline: Option Option>, + /// Executes after reading a REPL line (only available with `repl` feature) #[cfg(feature = "repl")] pub repl_post_readline: Option, + /// Executes before executing a REPL command (only available with `repl` feature) + #[cfg(feature = "repl")] + pub repl_pre_exec: Option, + + /// Executes after executing a REPL command (only available with `repl` feature) + #[cfg(feature = "repl")] + pub repl_post_exec: Option, + /// Executes when the REPL receives a render result (only available with `repl` feature) #[cfg(feature = "repl")] pub repl_on_receive_result: Option, @@ -59,6 +71,14 @@ where /// Executes when the REPL panics (only available with `repl` feature) #[cfg(all(feature = "repl", not(feature = "async")))] pub repl_on_panic: Option, + + /// Executes when the REPL exits (only available with `repl` feature) + #[cfg(feature = "repl")] + pub repl_exit: Option, + + /// Executes after each REPL loop iteration (only available with `repl` feature) + #[cfg(feature = "repl")] + pub repl_loop_once: Option, } impl Program @@ -214,6 +234,22 @@ where } } + /// Runs the custom REPL readline hook (only available with `repl` feature) + /// Returns `Some(line)` if a hook was set and returned Some, otherwise `None`. + #[cfg(feature = "repl")] + pub(crate) fn run_hook_repl_readline(&self) -> Option { + if !self.user_context.run_hook { + return None; + } + + for hook in &self.hooks { + if let Some(repl_readline) = hook.repl_readline { + return repl_readline(); + } + } + None + } + /// Runs the REPL post-readline hooks (only available with `repl` feature) #[cfg(feature = "repl")] pub(crate) fn run_hook_repl_post_readline(&self, line: &str) { @@ -228,6 +264,34 @@ where } } + /// Runs the REPL pre-exec hooks (only available with `repl` feature) + #[cfg(feature = "repl")] + pub(crate) fn run_hook_repl_pre_exec(&self, args: &[String]) { + if !self.user_context.run_hook { + return; + } + + for hook in &self.hooks { + if let Some(repl_pre_exec) = hook.repl_pre_exec { + repl_pre_exec(args) + } + } + } + + /// Runs the REPL post-exec hooks (only available with `repl` feature) + #[cfg(feature = "repl")] + pub(crate) fn run_hook_repl_post_exec(&self) { + if !self.user_context.run_hook { + return; + } + + for hook in &self.hooks { + if let Some(repl_post_exec) = hook.repl_post_exec { + repl_post_exec() + } + } + } + /// Runs the REPL receive result hooks (only available with `repl` feature) #[cfg(feature = "repl")] pub(crate) fn run_hook_repl_on_receive_result(&self, result: &RenderResult) { @@ -255,6 +319,34 @@ where } } } + + /// Runs the REPL exit hooks (only available with `repl` feature) + #[cfg(feature = "repl")] + pub(crate) fn run_hook_repl_exit(&self) { + if !self.user_context.run_hook { + return; + } + + for hook in &self.hooks { + if let Some(repl_exit) = hook.repl_exit { + repl_exit() + } + } + } + + /// Runs the REPL loop_once hooks (only available with `repl` feature) + #[cfg(feature = "repl")] + pub(crate) fn run_hook_repl_loop_once(&self) { + if !self.user_context.run_hook { + return; + } + + for hook in &self.hooks { + if let Some(repl_loop_once) = hook.repl_loop_once { + repl_loop_once() + } + } + } } impl ProgramHook @@ -279,11 +371,21 @@ where #[cfg(feature = "repl")] repl_pre_readline: None, #[cfg(feature = "repl")] + repl_readline: None, + #[cfg(feature = "repl")] repl_post_readline: None, #[cfg(feature = "repl")] + repl_pre_exec: None, + #[cfg(feature = "repl")] + repl_post_exec: None, + #[cfg(feature = "repl")] repl_on_receive_result: None, #[cfg(all(feature = "repl", not(feature = "async")))] repl_on_panic: None, + #[cfg(feature = "repl")] + repl_exit: None, + #[cfg(feature = "repl")] + repl_loop_once: None, } } @@ -357,6 +459,15 @@ where self } + /// Sets the custom REPL line reader (only available with `repl` feature). + /// If set, this function will be called to read a line instead of the default mechanism. + /// Returning `None` signals that there is no input (e.g., EOF). + #[cfg(feature = "repl")] + pub fn on_repl_readline(mut self, handler: fn() -> Option) -> Self { + let _ = self.repl_readline.insert(handler); + self + } + /// Sets the handler for the REPL post-readline event (only available with `repl` feature). /// This hook runs after reading a line of input and receives the line as a `&str`. #[cfg(feature = "repl")] @@ -365,7 +476,24 @@ where self } + /// Sets the handler for the REPL pre-exec event (only available with `repl` feature). + /// This hook runs before executing a REPL command, receiving the parsed arguments. + #[cfg(feature = "repl")] + pub fn on_repl_pre_exec(mut self, handler: fn(args: &[String])) -> Self { + let _ = self.repl_pre_exec.insert(handler); + self + } + + /// Sets the handler for the REPL post-exec event (only available with `repl` feature). + /// This hook runs after executing a REPL command. + #[cfg(feature = "repl")] + pub fn on_repl_post_exec(mut self, handler: fn()) -> Self { + let _ = self.repl_post_exec.insert(handler); + self + } + /// Sets the handler for the REPL receive result event (only available with `repl` feature). + /// This hook runs after a command is executed, receiving the render result on success. #[cfg(feature = "repl")] pub fn on_repl_receive_result(mut self, handler: fn(result: &RenderResult)) -> Self { let _ = self.repl_on_receive_result.insert(handler); @@ -378,4 +506,20 @@ where let _ = self.repl_on_panic.insert(handler); self } + + /// Sets the handler for the REPL exit event (only available with `repl` feature). + /// This hook runs when the REPL is about to exit. + #[cfg(feature = "repl")] + pub fn on_repl_exit(mut self, handler: fn()) -> Self { + let _ = self.repl_exit.insert(handler); + self + } + + /// Sets the handler for the REPL loop_once event (only available with `repl` feature). + /// This hook runs after each REPL loop iteration. + #[cfg(feature = "repl")] + pub fn on_repl_loop_once(mut self, handler: fn()) -> Self { + let _ = self.repl_loop_once.insert(handler); + self + } } diff --git a/mingling_core/src/program/repl_exec.rs b/mingling_core/src/program/repl_exec.rs index 5417252..c4232ab 100644 --- a/mingling_core/src/program/repl_exec.rs +++ b/mingling_core/src/program/repl_exec.rs @@ -31,11 +31,12 @@ where self.exec_wrapper(|p| -> () { loop { p.run_hook_repl_pre_readline(); - let readline = readline_or_empty(); + let readline = p.run_hook_repl_readline().unwrap_or_default(); p.run_hook_repl_post_readline(&readline); let args = split_input_string(readline.clone()); + p.run_hook_repl_pre_exec(&args); match exec_once(p, args) { Ok(r) => { p.run_hook_repl_on_receive_result(&r); @@ -45,10 +46,14 @@ where } _ => {} } + p.run_hook_repl_post_exec(); if this::().res::().unwrap().exit { + p.run_hook_repl_exit(); break; } + + p.run_hook_repl_loop_once(); } }); } @@ -75,38 +80,32 @@ where self.exec_wrapper(async |p| -> () { loop { p.run_hook_repl_pre_readline(); - let readline = readline_or_empty(); + let readline = p.run_hook_repl_readline().unwrap_or_default(); p.run_hook_repl_post_readline(&readline); let args = split_input_string(readline.clone()); + p.run_hook_repl_pre_exec(&args); match exec_once(p, args).await { Ok(r) => { p.run_hook_repl_on_receive_result(&r); } _ => {} } + p.run_hook_repl_post_exec(); if this::().res::().unwrap().exit { + p.run_hook_repl_exit(); break; } + + p.run_hook_repl_loop_once(); } }) .await; } } -fn readline() -> Result { - let mut input = String::new(); - std::io::stdout().flush()?; - std::io::stdin().read_line(&mut input)?; - Ok(input.trim().to_string()) -} - -fn readline_or_empty() -> String { - readline().unwrap_or("".to_string()) -} - #[cfg(not(feature = "async"))] fn exec_once( p: &'static Program, -- cgit