diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-24 12:01:38 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-24 12:01:38 +0800 |
| commit | 1d1baf75a3acb5eb32913a8bdad42bae42844aa2 (patch) | |
| tree | 5dafbcbce48a5de3c61bba0c7d309e97dd80b1ce /mingling_core/src/program/hook.rs | |
| parent | addfbbf0b33a6251605990da73c2de5131766827 (diff) | |
Redesign hook system with structured info types and ProgramControls
Diffstat (limited to 'mingling_core/src/program/hook.rs')
| -rw-r--r-- | mingling_core/src/program/hook.rs | 470 |
1 files changed, 291 insertions, 179 deletions
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs index f6df2e2..7b07d90 100644 --- a/mingling_core/src/program/hook.rs +++ b/mingling_core/src/program/hook.rs @@ -1,84 +1,96 @@ #![allow(dead_code)] -use std::any::Any; +use crate::{Program, ProgramCollect}; -use crate::{AnyOutput, Program, ProgramCollect, RenderResult}; +mod hook_info; +pub use hook_info::*; -#[cfg(not(feature = "async"))] -use crate::error::ProgramPanic; +mod control_unit; +pub use control_unit::*; #[derive(Default)] +#[allow(clippy::type_complexity)] // Shutup! pub struct ProgramHook<C> where C: ProgramCollect<Enum = C>, { /// Executes when the program starts running - pub begin: Option<fn()>, + pub begin: Option<Box<dyn Fn(&HookBeginInfo) + Send + Sync>>, /// Executes before the program dispatches - pub pre_dispatch: Option<fn(args: &[String])>, + pub pre_dispatch: + Option<Box<dyn for<'a> Fn(&HookPreDispatchInfo<'a>) -> ProgramControls<C> + Send + Sync>>, /// Executes after the program dispatches - pub post_dispatch: Option<fn(entry: &C)>, + pub post_dispatch: Option< + Box<dyn for<'a> Fn(&HookPostDispatchInfo<'a, C>) -> ProgramControls<C> + Send + Sync>, + >, /// Executes before the type enters the chain - pub pre_chain: Option<fn(input: &C, raw: &dyn Any)>, + pub pre_chain: + Option<Box<dyn for<'a> Fn(&HookPreChainInfo<'a, C>) -> ProgramControls<C> + Send + Sync>>, /// Executes after the chain processing for the type ends - pub post_chain: Option<fn(output: &AnyOutput<C>)>, + pub post_chain: + Option<Box<dyn for<'a> Fn(&HookPostChainInfo<'a, C>) -> ProgramControls<C> + Send + Sync>>, /// Executes before the type enters the renderer - pub pre_render: Option<fn(input: &C, raw: &dyn Any)>, + pub pre_render: + Option<Box<dyn for<'a> Fn(&HookPreRenderInfo<'a, C>) -> ProgramControls<C> + Send + Sync>>, /// Executes after the type enters the renderer - pub post_render: Option<fn(result: &RenderResult)>, + pub post_render: + Option<Box<dyn for<'a> Fn(&HookPostRenderInfo<'a>) -> ProgramControls<C> + Send + Sync>>, /// Executes before the program ends - pub finish: Option<fn() -> i32>, + pub finish: Option<Box<dyn Fn(&HookFinishInfo) -> ProgramControls<C> + Send + Sync>>, /// Executes when the program panics #[cfg(not(feature = "async"))] - pub exec_panic: Option<fn(&ProgramPanic)>, + pub exec_panic: Option<Box<dyn for<'a> Fn(&HookPanicInfo<'a>) + Send + Sync>>, /// Executes when the REPL starts (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_on_begin: Option<fn()>, + pub repl_on_begin: Option<Box<dyn Fn(&HookREPLBeginInfo) + Send + Sync>>, /// Executes before reading the next REPL line (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_pre_readline: Option<fn()>, + pub repl_pre_readline: Option<Box<dyn Fn(&HookREPLPreReadlineInfo) + Send + Sync>>, /// Custom REPL line reader (only available with `repl` feature) + /// Returns `Some(line)` to provide a custom input line. #[cfg(feature = "repl")] - pub repl_readline: Option<fn() -> Option<String>>, + pub repl_readline: Option<Box<dyn Fn(&HookREPLReadlineInfo) -> Option<String> + Send + Sync>>, /// Executes after reading a REPL line (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_post_readline: Option<fn(line: &mut String)>, + pub repl_post_readline: + Option<Box<dyn for<'a> Fn(&HookREPLPostReadlineInfo<'a>) + Send + Sync>>, /// Executes before executing a REPL command (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_pre_exec: Option<fn(args: &[String])>, + pub repl_pre_exec: Option<Box<dyn for<'a> Fn(&HookREPLPreExecInfo<'a>) + Send + Sync>>, /// Executes after executing a REPL command (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_post_exec: Option<fn()>, + pub repl_post_exec: Option<Box<dyn Fn(&HookREPLPostExecInfo) + Send + Sync>>, /// Executes when the REPL receives a render result (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_on_receive_result: Option<fn(&RenderResult)>, + pub repl_on_receive_result: + Option<Box<dyn for<'a> Fn(&HookREPLOnReceiveResultInfo<'a>) + Send + Sync>>, /// Executes when the REPL panics (only available with `repl` feature) #[cfg(all(feature = "repl", not(feature = "async")))] - pub repl_on_panic: Option<fn(&ProgramPanic)>, + pub repl_on_panic: Option<Box<dyn for<'a> Fn(&HookREPLOnPanicInfo<'a>) + Send + Sync>>, /// Executes when the REPL exits (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_exit: Option<fn()>, + pub repl_exit: Option<Box<dyn Fn(&HookREPLExitInfo) + Send + Sync>>, /// Executes after each REPL loop iteration (only available with `repl` feature) #[cfg(feature = "repl")] - pub repl_loop_once: Option<fn()>, + pub repl_loop_once: Option<Box<dyn Fn(&HookREPLLoopOnceInfo) + Send + Sync>>, } impl<C> Program<C> @@ -91,145 +103,157 @@ where self.hooks.push(hook); } - pub(crate) fn run_hook_on_begin(&self) { + pub(crate) fn run_hook_on_begin(&self, info: HookBeginInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(begin) = hook.begin { - begin(); + if let Some(ref begin) = hook.begin { + begin(&info); } } } - pub(crate) fn run_hook_pre_dispatch(&self, args: &[String]) { + pub(crate) fn run_hook_pre_dispatch(&self, info: HookPreDispatchInfo) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(pre_dispatch) = hook.pre_dispatch { - pre_dispatch(args); + if let Some(ref pre_dispatch) = hook.pre_dispatch { + controls = pre_dispatch(&info); } } + controls } - pub(crate) fn run_hook_post_dispatch(&self, entry: &C) { + pub(crate) fn run_hook_post_dispatch( + &self, + info: HookPostDispatchInfo<C>, + ) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(post_dispatch) = hook.post_dispatch { - post_dispatch(entry); + if let Some(ref post_dispatch) = hook.post_dispatch { + controls = post_dispatch(&info); } } + controls } - pub(crate) fn run_hook_pre_chain(&self, input: &C, raw: &dyn Any) { + pub(crate) fn run_hook_pre_chain(&self, info: HookPreChainInfo<C>) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(pre_chain) = hook.pre_chain { - pre_chain(input, raw); + if let Some(ref pre_chain) = hook.pre_chain { + controls = pre_chain(&info); } } + controls } - pub(crate) fn run_hook_post_chain(&self, output: &AnyOutput<C>) { + pub(crate) fn run_hook_post_chain(&self, info: HookPostChainInfo<C>) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(post_chain) = hook.post_chain { - post_chain(output); + if let Some(ref post_chain) = hook.post_chain { + controls = post_chain(&info); } } + controls } - pub(crate) fn run_hook_pre_render(&self, input: &C, raw: &dyn Any) { + pub(crate) fn run_hook_pre_render(&self, info: HookPreRenderInfo<C>) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(pre_render) = hook.pre_render { - pre_render(input, raw); + if let Some(ref pre_render) = hook.pre_render { + controls = pre_render(&info); } } + controls } - pub(crate) fn run_hook_post_render(&self, result: &RenderResult) { + pub(crate) fn run_hook_post_render(&self, info: HookPostRenderInfo) -> ProgramControls<C> { if !self.user_context.run_hook { - return; + return ProgramControls::Empty; } + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(post_render) = hook.post_render { - post_render(result); + if let Some(ref post_render) = hook.post_render { + controls = post_render(&info); } } + controls } #[allow(dead_code)] #[cfg(not(feature = "async"))] - pub(crate) fn run_hook_exec_panic(&self, panic_info: &ProgramPanic) { + pub(crate) fn run_hook_exec_panic(&self, info: HookPanicInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(exec_panic) = hook.exec_panic { - exec_panic(panic_info); + if let Some(ref exec_panic) = hook.exec_panic { + exec_panic(&info); } } } - pub(crate) fn run_hook_finish(&self) -> i32 { + pub(crate) fn run_hook_finish(&self, info: HookFinishInfo) -> ProgramControls<C> { if !self.user_context.run_hook { - return 0; + return ProgramControls::Empty; } - let mut exit_code = 0; + let mut controls = ProgramControls::Empty; for hook in &self.hooks { - if let Some(finish) = hook.finish { - exit_code = finish(); - if exit_code != 0 { - return exit_code; - } + if let Some(ref finish) = hook.finish { + controls = finish(&info); } } - exit_code + controls } /// Runs the REPL begin hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_on_begin(&self) { + pub(crate) fn run_hook_repl_on_begin(&self, info: HookREPLBeginInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_on_begin) = hook.repl_on_begin { - repl_on_begin() + if let Some(ref repl_on_begin) = hook.repl_on_begin { + repl_on_begin(&info); } } } /// Runs the REPL pre-readline hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_pre_readline(&self) { + pub(crate) fn run_hook_repl_pre_readline(&self, info: HookREPLPreReadlineInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_pre_readline) = hook.repl_pre_readline { - repl_pre_readline() + if let Some(ref repl_pre_readline) = hook.repl_pre_readline { + repl_pre_readline(&info); } } } @@ -237,14 +261,14 @@ 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<String> { + pub(crate) fn run_hook_repl_readline(&self, info: HookREPLReadlineInfo) -> Option<String> { if !self.user_context.run_hook { return None; } for hook in &self.hooks { - if let Some(repl_readline) = hook.repl_readline { - return repl_readline(); + if let Some(ref repl_readline) = hook.repl_readline { + return repl_readline(&info); } } None @@ -252,98 +276,98 @@ where /// Runs the REPL post-readline hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_post_readline(&self, line: &mut String) { + pub(crate) fn run_hook_repl_post_readline(&self, info: HookREPLPostReadlineInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_post_readline) = hook.repl_post_readline { - repl_post_readline(line) + if let Some(ref repl_post_readline) = hook.repl_post_readline { + repl_post_readline(&info); } } } /// 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]) { + pub(crate) fn run_hook_repl_pre_exec(&self, info: HookREPLPreExecInfo) { 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) + if let Some(ref repl_pre_exec) = hook.repl_pre_exec { + repl_pre_exec(&info); } } } /// Runs the REPL post-exec hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_post_exec(&self) { + pub(crate) fn run_hook_repl_post_exec(&self, info: HookREPLPostExecInfo) { 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() + if let Some(ref repl_post_exec) = hook.repl_post_exec { + repl_post_exec(&info); } } } /// 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) { + pub(crate) fn run_hook_repl_on_receive_result(&self, info: HookREPLOnReceiveResultInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_on_receive_result) = hook.repl_on_receive_result { - repl_on_receive_result(result) + if let Some(ref repl_on_receive_result) = hook.repl_on_receive_result { + repl_on_receive_result(&info); } } } /// Runs the REPL panic hooks (only available with `repl` feature) #[cfg(all(feature = "repl", not(feature = "async")))] - pub(crate) fn run_hook_repl_on_panic(&self, panic_info: &ProgramPanic) { + pub(crate) fn run_hook_repl_on_panic(&self, info: HookREPLOnPanicInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_on_panic) = hook.repl_on_panic { - repl_on_panic(panic_info) + if let Some(ref repl_on_panic) = hook.repl_on_panic { + repl_on_panic(&info); } } } /// Runs the REPL exit hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_exit(&self) { + pub(crate) fn run_hook_repl_exit(&self, info: HookREPLExitInfo) { if !self.user_context.run_hook { return; } for hook in &self.hooks { - if let Some(repl_exit) = hook.repl_exit { - repl_exit() + if let Some(ref repl_exit) = hook.repl_exit { + repl_exit(&info); } } } /// Runs the REPL loop_once hooks (only available with `repl` feature) #[cfg(feature = "repl")] - pub(crate) fn run_hook_repl_loop_once(&self) { + pub(crate) fn run_hook_repl_loop_once(&self, info: HookREPLLoopOnceInfo) { 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() + if let Some(ref repl_loop_once) = hook.repl_loop_once { + repl_loop_once(&info); } } } @@ -392,73 +416,110 @@ where /// Sets the handler for the `begin` event. #[must_use] - pub fn on_begin(mut self, handler: fn()) -> Self { - let _ = self.begin.insert(handler); + pub fn on_begin<F, R>(mut self, handler: F) -> Self + where + F: Fn(&HookBeginInfo) + 'static + Send + Sync, + { + self.begin = Some(Box::new(move |info| handler(info))); self } /// Sets the handler for the `pre_dispatch` event. #[must_use] - pub fn on_pre_dispatch(mut self, handler: fn(args: &[String])) -> Self { - let _ = self.pre_dispatch.insert(handler); + pub fn on_pre_dispatch<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPreDispatchInfo<'a>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.pre_dispatch = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `post_dispatch` event. #[must_use] - pub fn on_post_dispatch(mut self, handler: fn(entry: &C)) -> Self { - let _ = self.post_dispatch.insert(handler); + pub fn on_post_dispatch<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPostDispatchInfo<'a, C>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.post_dispatch = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `pre_chain` event. #[must_use] - pub fn on_pre_chain(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self { - let _ = self.pre_chain.insert(handler); + pub fn on_pre_chain<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPreChainInfo<'a, C>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.pre_chain = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `post_chain` event. #[must_use] - pub fn on_post_chain(mut self, handler: fn(output: &AnyOutput<C>)) -> Self { - let _ = self.post_chain.insert(handler); + pub fn on_post_chain<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPostChainInfo<'a, C>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.post_chain = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `pre_render` event. #[must_use] - pub fn on_pre_render(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self { - let _ = self.pre_render.insert(handler); + pub fn on_pre_render<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPreRenderInfo<'a, C>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.pre_render = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `post_render` event. #[must_use] - pub fn on_post_render(mut self, handler: fn(result: &RenderResult)) -> Self { - let _ = self.post_render.insert(handler); + pub fn on_post_render<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPostRenderInfo<'a>) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.post_render = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `finish` event. #[must_use] - pub fn on_finish(mut self, handler: fn() -> i32) -> Self { - let _ = self.finish.insert(handler); + pub fn on_finish<F, R>(mut self, handler: F) -> Self + where + F: Fn(&HookFinishInfo) -> R + 'static + Send + Sync, + R: Into<ProgramControls<C>>, + { + self.finish = Some(Box::new(move |info| handler(info).into())); self } /// Sets the handler for the `exec_panic` event. #[cfg(not(feature = "async"))] #[must_use] - pub fn on_exec_panic(mut self, handler: fn(&ProgramPanic)) -> Self { - let _ = self.exec_panic.insert(handler); + pub fn on_exec_panic<F, R>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookPanicInfo<'a>) + 'static + Send + Sync, + { + self.exec_panic = Some(Box::new(move |info| handler(info))); self } /// Sets the handler for the REPL begin event (only available with `repl` feature). #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_begin(mut self, handler: fn()) -> Self { - let _ = self.repl_on_begin.insert(handler); + pub fn on_repl_begin<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLBeginInfo) + 'static + Send + Sync, + { + self.repl_on_begin = Some(Box::new(move |info| handler(info))); self } @@ -466,27 +527,34 @@ where /// This hook runs after `on_repl_begin` but before reading the next input line. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_pre_readline(mut self, handler: fn()) -> Self { - let _ = self.repl_pre_readline.insert(handler); + pub fn on_repl_pre_readline<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLPreReadlineInfo) + 'static + Send + Sync, + { + self.repl_pre_readline = Some(Box::new(move |info| handler(info))); 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")] #[must_use] - pub fn on_repl_readline(mut self, handler: fn() -> Option<String>) -> Self { - let _ = self.repl_readline.insert(handler); + pub fn on_repl_readline<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLReadlineInfo) -> Option<String> + 'static + Send + Sync, + { + self.repl_readline = Some(Box::new(move |info| handler(info))); 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 a mutable reference to the line. + /// This hook runs after reading a line of input and receives the read line info. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_post_readline(mut self, handler: fn(line: &mut String)) -> Self { - let _ = self.repl_post_readline.insert(handler); + pub fn on_repl_post_readline<F>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookREPLPostReadlineInfo<'a>) + 'static + Send + Sync, + { + self.repl_post_readline = Some(Box::new(move |info| handler(info))); self } @@ -494,8 +562,11 @@ where /// This hook runs before executing a REPL command, receiving the parsed arguments. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_pre_exec(mut self, handler: fn(args: &[String])) -> Self { - let _ = self.repl_pre_exec.insert(handler); + pub fn on_repl_pre_exec<F>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookREPLPreExecInfo<'a>) + 'static + Send + Sync, + { + self.repl_pre_exec = Some(Box::new(move |info| handler(info))); self } @@ -503,8 +574,11 @@ where /// This hook runs after executing a REPL command. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_post_exec(mut self, handler: fn()) -> Self { - let _ = self.repl_post_exec.insert(handler); + pub fn on_repl_post_exec<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLPostExecInfo) + 'static + Send + Sync, + { + self.repl_post_exec = Some(Box::new(move |info| handler(info))); self } @@ -512,16 +586,22 @@ where /// This hook runs after a command is executed, receiving the render result on success. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_receive_result(mut self, handler: fn(result: &RenderResult)) -> Self { - let _ = self.repl_on_receive_result.insert(handler); + pub fn on_repl_receive_result<F>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookREPLOnReceiveResultInfo<'a>) + 'static + Send + Sync, + { + self.repl_on_receive_result = Some(Box::new(move |info| handler(info))); self } /// Sets the handler for the REPL panic event (only available with `repl` feature). #[cfg(all(feature = "repl", not(feature = "async")))] #[must_use] - pub fn on_repl_panic(mut self, handler: fn(panic: &ProgramPanic)) -> Self { - let _ = self.repl_on_panic.insert(handler); + pub fn on_repl_panic<F>(mut self, handler: F) -> Self + where + F: for<'a> Fn(&HookREPLOnPanicInfo<'a>) + 'static + Send + Sync, + { + self.repl_on_panic = Some(Box::new(move |info| handler(info))); self } @@ -529,8 +609,11 @@ where /// This hook runs when the REPL is about to exit. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_exit(mut self, handler: fn()) -> Self { - let _ = self.repl_exit.insert(handler); + pub fn on_repl_exit<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLExitInfo) + 'static + Send + Sync, + { + self.repl_exit = Some(Box::new(move |info| handler(info))); self } @@ -538,8 +621,11 @@ where /// This hook runs after each REPL loop iteration. #[cfg(feature = "repl")] #[must_use] - pub fn on_repl_loop_once(mut self, handler: fn()) -> Self { - let _ = self.repl_loop_once.insert(handler); + pub fn on_repl_loop_once<F>(mut self, handler: F) -> Self + where + F: Fn(&HookREPLLoopOnceInfo) + 'static + Send + Sync, + { + self.repl_loop_once = Some(Box::new(move |info| handler(info))); self } } @@ -575,46 +661,49 @@ mod tests { type ErrorRendererNotFound = MockHookEnum; type ResultEmpty = MockHookEnum; - fn build_renderer_not_found(_member_id: MockHookEnum) -> AnyOutput<MockHookEnum> { + fn build_renderer_not_found(_member_id: MockHookEnum) -> crate::AnyOutput<MockHookEnum> { unreachable!() } - fn build_dispatcher_not_found(_args: Vec<String>) -> AnyOutput<MockHookEnum> { + fn build_dispatcher_not_found(_args: Vec<String>) -> crate::AnyOutput<MockHookEnum> { unreachable!() } - fn build_empty_result() -> AnyOutput<MockHookEnum> { + fn build_empty_result() -> crate::AnyOutput<MockHookEnum> { unreachable!() } - fn render(_any: AnyOutput<MockHookEnum>, _r: &mut RenderResult) { + fn render(_any: crate::AnyOutput<MockHookEnum>, _r: &mut crate::RenderResult) { unreachable!() } - fn render_help(_any: AnyOutput<MockHookEnum>, _r: &mut RenderResult) { + fn render_help(_any: crate::AnyOutput<MockHookEnum>, _r: &mut crate::RenderResult) { unreachable!() } - fn do_chain(_any: AnyOutput<MockHookEnum>) -> crate::ChainProcess<MockHookEnum> { + fn do_chain(_any: crate::AnyOutput<MockHookEnum>) -> crate::ChainProcess<MockHookEnum> { unreachable!() } - fn has_renderer(_any: &AnyOutput<MockHookEnum>) -> bool { + fn has_renderer(_any: &crate::AnyOutput<MockHookEnum>) -> bool { unreachable!() } - fn has_chain(_any: &AnyOutput<MockHookEnum>) -> bool { + fn has_chain(_any: &crate::AnyOutput<MockHookEnum>) -> bool { unreachable!() } #[cfg(feature = "comp")] - fn do_comp(_any: &AnyOutput<MockHookEnum>, _ctx: &crate::ShellContext) -> crate::Suggest { + fn do_comp( + _any: &crate::AnyOutput<MockHookEnum>, + _ctx: &crate::ShellContext, + ) -> crate::Suggest { unreachable!() } #[cfg(feature = "general_renderer")] fn general_render( - _any: AnyOutput<MockHookEnum>, + _any: crate::AnyOutput<MockHookEnum>, _setting: &crate::GeneralRendererSetting, ) -> Result<crate::RenderResult, crate::error::GeneralRendererSerializeError> { unreachable!() @@ -637,35 +726,42 @@ mod tests { #[test] fn test_hook_on_begin() { static CALLED: AtomicBool = AtomicBool::new(false); - let hook = ProgramHook::<MockHookEnum>::empty().on_begin(|| { + let hook = ProgramHook::<MockHookEnum>::empty().on_begin::<_, ()>(|_: &HookBeginInfo| { CALLED.store(true, Ordering::SeqCst); }); assert!(hook.begin.is_some()); - (hook.begin.unwrap())(); + (hook.begin.as_ref().unwrap())(&HookBeginInfo {}); assert!(CALLED.load(Ordering::SeqCst)); } #[test] fn test_hook_on_pre_dispatch() { static CALLED: AtomicBool = AtomicBool::new(false); - let hook = ProgramHook::<MockHookEnum>::empty().on_pre_dispatch(|args| { - assert_eq!(args, &["a", "b"]); - CALLED.store(true, Ordering::SeqCst); - }); + let hook = + ProgramHook::<MockHookEnum>::empty().on_pre_dispatch(|info: &HookPreDispatchInfo| { + assert_eq!(info.arguments, &["a", "b"]); + CALLED.store(true, Ordering::SeqCst); + }); assert!(hook.pre_dispatch.is_some()); - (hook.pre_dispatch.unwrap())(&["a".to_string(), "b".to_string()]); + (hook.pre_dispatch.as_ref().unwrap())(&HookPreDispatchInfo { + arguments: &["a".to_string(), "b".to_string()], + }); assert!(CALLED.load(Ordering::SeqCst)); } #[test] fn test_hook_on_post_dispatch() { static CALLED: AtomicBool = AtomicBool::new(false); - let hook = ProgramHook::<MockHookEnum>::empty().on_post_dispatch(|entry| { - assert_eq!(*entry, MockHookEnum::A); - CALLED.store(true, Ordering::SeqCst); - }); + let hook = ProgramHook::<MockHookEnum>::empty().on_post_dispatch( + |info: &HookPostDispatchInfo<MockHookEnum>| { + assert_eq!(*info.entry, MockHookEnum::A); + CALLED.store(true, Ordering::SeqCst); + }, + ); assert!(hook.post_dispatch.is_some()); - (hook.post_dispatch.unwrap())(&MockHookEnum::A); + (hook.post_dispatch.as_ref().unwrap())(&HookPostDispatchInfo { + entry: &MockHookEnum::A, + }); assert!(CALLED.load(Ordering::SeqCst)); } @@ -673,13 +769,16 @@ mod tests { fn test_hook_on_pre_chain() { static CALLED: AtomicBool = AtomicBool::new(false); let hook = ProgramHook::<MockHookEnum>::empty().on_pre_chain( - |input: &MockHookEnum, _raw: &dyn Any| { - assert_eq!(*input, MockHookEnum::A); + |info: &HookPreChainInfo<MockHookEnum>| { + assert_eq!(*info.input, MockHookEnum::A); CALLED.store(true, Ordering::SeqCst); }, ); assert!(hook.pre_chain.is_some()); - (hook.pre_chain.unwrap())(&MockHookEnum::A, &42); + (hook.pre_chain.as_ref().unwrap())(&HookPreChainInfo { + input: &MockHookEnum::A, + raw: &42, + }); assert!(CALLED.load(Ordering::SeqCst)); } @@ -687,13 +786,13 @@ mod tests { fn test_hook_on_post_chain() { static CALLED: AtomicBool = AtomicBool::new(false); let hook = ProgramHook::<MockHookEnum>::empty().on_post_chain( - |_output: &AnyOutput<MockHookEnum>| { + |_info: &HookPostChainInfo<MockHookEnum>| { CALLED.store(true, Ordering::SeqCst); }, ); assert!(hook.post_chain.is_some()); - let output = AnyOutput::new(MockHookEnum::A); - (hook.post_chain.unwrap())(&output); + let output = crate::AnyOutput::new(MockHookEnum::A); + (hook.post_chain.as_ref().unwrap())(&HookPostChainInfo { output: &output }); assert!(CALLED.load(Ordering::SeqCst)); } @@ -701,46 +800,59 @@ mod tests { fn test_hook_on_pre_render() { static CALLED: AtomicBool = AtomicBool::new(false); let hook = ProgramHook::<MockHookEnum>::empty().on_pre_render( - |input: &MockHookEnum, _raw: &dyn Any| { - assert_eq!(*input, MockHookEnum::A); + |info: &HookPreRenderInfo<MockHookEnum>| { + assert_eq!(*info.input, MockHookEnum::A); CALLED.store(true, Ordering::SeqCst); }, ); assert!(hook.pre_render.is_some()); - (hook.pre_render.unwrap())(&MockHookEnum::A, &42); + (hook.pre_render.as_ref().unwrap())(&HookPreRenderInfo { + input: &MockHookEnum::A, + raw: &42, + }); assert!(CALLED.load(Ordering::SeqCst)); } #[test] fn test_hook_on_post_render() { static CALLED: AtomicBool = AtomicBool::new(false); - let hook = ProgramHook::<MockHookEnum>::empty().on_post_render(|_result: &RenderResult| { - CALLED.store(true, Ordering::SeqCst); - }); + let hook = + ProgramHook::<MockHookEnum>::empty().on_post_render(|_info: &HookPostRenderInfo| { + CALLED.store(true, Ordering::SeqCst); + }); assert!(hook.post_render.is_some()); - let result = RenderResult::default(); - (hook.post_render.unwrap())(&result); + let result = crate::RenderResult::default(); + (hook.post_render.as_ref().unwrap())(&HookPostRenderInfo { result: &result }); assert!(CALLED.load(Ordering::SeqCst)); } #[test] fn test_hook_on_finish() { - let hook = ProgramHook::<MockHookEnum>::empty().on_finish(|| 42); + let hook = ProgramHook::<MockHookEnum>::empty() + .on_finish(|_: &HookFinishInfo| ProgramControlUnit::OverrideExitCode(42)); assert!(hook.finish.is_some()); - assert_eq!((hook.finish.unwrap())(), 42); + let controls: Vec<ProgramControlUnit<MockHookEnum>> = + (hook.finish.as_ref().unwrap())(&HookFinishInfo {}) + .into_iter() + .collect(); + assert_eq!(controls.len(), 1); + match &controls[0] { + ProgramControlUnit::OverrideExitCode(code) => assert_eq!(*code, 42), + _ => panic!("Unexpected control unit"), + } } #[test] fn test_hook_builder_chaining() { let hook = ProgramHook::<MockHookEnum>::empty() - .on_begin(|| {}) - .on_pre_dispatch(|_| {}) - .on_post_dispatch(|_| {}) - .on_pre_chain(|_, _| {}) - .on_post_chain(|_| {}) - .on_pre_render(|_, _| {}) - .on_post_render(|_| {}) - .on_finish(|| 0); + .on_begin::<_, ()>(|_: &HookBeginInfo| ()) + .on_pre_dispatch(|_: &HookPreDispatchInfo| ()) + .on_post_dispatch(|_: &HookPostDispatchInfo<MockHookEnum>| ()) + .on_pre_chain(|_: &HookPreChainInfo<MockHookEnum>| ()) + .on_post_chain(|_: &HookPostChainInfo<MockHookEnum>| ()) + .on_pre_render(|_: &HookPreRenderInfo<MockHookEnum>| ()) + .on_post_render(|_: &HookPostRenderInfo| ()) + .on_finish(|_: &HookFinishInfo| ProgramControlUnit::OverrideExitCode(0)); assert!(hook.begin.is_some()); assert!(hook.pre_dispatch.is_some()); assert!(hook.post_dispatch.is_some()); |
