From 18c5c3fd34ceb8a1631f7766b69e407cf92e1a09 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Fri, 15 May 2026 21:54:11 +0800 Subject: Add panic catch for program execution --- mingling_core/src/asset/chain/error.rs | 16 +---------- mingling_core/src/lib.rs | 2 +- mingling_core/src/program.rs | 50 +++++++++++++++++++++++++++++---- mingling_core/src/program/error.rs | 22 +++++++++++++++ mingling_core/src/program/exec/error.rs | 5 +++- mingling_core/src/program/hook.rs | 24 +++++++++++++++- 6 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 mingling_core/src/program/error.rs (limited to 'mingling_core') diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs index 5221e57..e5c753d 100644 --- a/mingling_core/src/asset/chain/error.rs +++ b/mingling_core/src/asset/chain/error.rs @@ -1,4 +1,4 @@ -use crate::error::{ProgramExecuteError, ProgramInternalExecuteError}; +use crate::error::ProgramInternalExecuteError; #[derive(thiserror::Error, Debug)] pub enum ChainProcessError { @@ -9,20 +9,6 @@ pub enum ChainProcessError { IO(#[from] std::io::Error), } -impl From for ChainProcessError { - fn from(value: ProgramExecuteError) -> Self { - match value { - ProgramExecuteError::DispatcherNotFound => { - ChainProcessError::Other("DispatcherNotFound".into()) - } - ProgramExecuteError::RendererNotFound(r) => { - ChainProcessError::Other(format!("RendererNotFound: {}", r)) - } - ProgramExecuteError::Other(e) => ChainProcessError::Other(e), - } - } -} - impl From for ChainProcessError { fn from(value: ProgramInternalExecuteError) -> Self { match value { diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs index 817cc7a..373df52 100644 --- a/mingling_core/src/lib.rs +++ b/mingling_core/src/lib.rs @@ -12,7 +12,6 @@ mod any; mod asset; mod program; mod renderer; - mod tester; /// Provides a toolkit for `Mingling` testing capabilities. @@ -38,6 +37,7 @@ pub use crate::asset::renderer::*; pub mod error { pub use crate::asset::chain::error::*; pub use crate::exec::error::*; + pub use crate::program::error::*; #[cfg(feature = "general_renderer")] pub use crate::renderer::general::error::*; } diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs index f2809f7..e7e9ec0 100644 --- a/mingling_core/src/program.rs +++ b/mingling_core/src/program.rs @@ -1,3 +1,5 @@ +use crate::error::ProgramPanic; + #[cfg(feature = "comp")] use crate::{ShellContext, Suggest}; @@ -21,6 +23,8 @@ use std::{ #[cfg(feature = "async")] use std::pin::Pin; +#[doc(hidden)] +pub mod error; #[doc(hidden)] pub mod exec; #[doc(hidden)] @@ -175,7 +179,7 @@ impl Program where C: ProgramCollect, { - async fn exec_wrapper(self, f: F) -> Fut::Output + async fn exec_wrapper(self, f: F) -> Result where C: 'static + Send + Sync, F: FnOnce(&'static Program) -> Fut + Send + Sync, @@ -189,7 +193,16 @@ where .unwrap() .downcast_ref::>() .unwrap(); - f(program).await + 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 @@ -198,8 +211,13 @@ where C: 'static + Send + Sync, { self.args = self.args.iter().skip(1).cloned().collect(); - self.exec_wrapper(|p| async { crate::exec::exec(p).await.map_err(|e| e.into()) }) + 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 @@ -223,6 +241,10 @@ where eprintln!("{}", e); return 1; } + ProgramExecuteError::Panic(unwinded_error) => { + eprintln!("{}", unwinded_error); + return 1; + } }, }; @@ -259,7 +281,7 @@ impl Program where C: ProgramCollect, { - fn exec_wrapper(self, f: F) -> R + fn exec_wrapper(self, f: F) -> Result where C: 'static + Send + Sync, F: FnOnce(&'static Program) -> R + Send + Sync, @@ -272,7 +294,16 @@ where .unwrap() .downcast_ref::>() .unwrap(); - f(program) + 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 @@ -281,7 +312,10 @@ where C: 'static + Send + Sync, { self.args = self.args.iter().skip(1).cloned().collect(); - self.exec_wrapper(|p| crate::exec::exec(p).map_err(|e| e.into())) + 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 @@ -305,6 +339,10 @@ where eprintln!("{}", e); return 1; } + ProgramExecuteError::Panic(unwinded_error) => { + eprintln!("{}", unwinded_error); + return 1; + } }, }; diff --git a/mingling_core/src/program/error.rs b/mingling_core/src/program/error.rs new file mode 100644 index 0000000..5e92a42 --- /dev/null +++ b/mingling_core/src/program/error.rs @@ -0,0 +1,22 @@ +use std::any::Any; +use std::fmt; +use thiserror::Error; + +/// Error type returned when a panic occurs during execution. +#[derive(Error)] +#[error("execution panicked: {payload:?}")] +pub struct ProgramPanic { + pub payload: Box, +} + +impl ProgramPanic { + pub fn new(payload: Box) -> Self { + ProgramPanic { payload } + } +} + +impl fmt::Debug for ProgramPanic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.payload) + } +} diff --git a/mingling_core/src/program/exec/error.rs b/mingling_core/src/program/exec/error.rs index b4ff378..2d806dc 100644 --- a/mingling_core/src/program/exec/error.rs +++ b/mingling_core/src/program/exec/error.rs @@ -1,4 +1,4 @@ -use crate::error::ChainProcessError; +use crate::error::{ChainProcessError, ProgramPanic}; #[derive(thiserror::Error, Debug)] pub enum ProgramExecuteError { @@ -8,6 +8,9 @@ pub enum ProgramExecuteError { #[error("No Renderer (`{0}`) Found")] RendererNotFound(String), + #[error("Panic: {0:?}")] + Panic(#[from] ProgramPanic), + #[error("Other error: {0}")] Other(String), } diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs index afc253c..080c00f 100644 --- a/mingling_core/src/program/hook.rs +++ b/mingling_core/src/program/hook.rs @@ -1,6 +1,6 @@ use std::any::Any; -use crate::{AnyOutput, Program, ProgramCollect, RenderResult}; +use crate::{AnyOutput, Program, ProgramCollect, RenderResult, error::ProgramPanic}; #[derive(Default)] pub struct ProgramHook @@ -30,6 +30,9 @@ where /// Executes before the program ends pub finish: Option i32>, + + /// Executes when the program panics + pub exec_panic: Option, } impl Program @@ -126,6 +129,18 @@ where } } + pub(crate) fn run_hook_exec_panic(&self, panic_info: &ProgramPanic) { + if !self.user_context.run_hook { + return; + } + + for hook in &self.hooks { + if let Some(exec_panic) = hook.exec_panic { + exec_panic(panic_info) + } + } + } + pub(crate) fn run_hook_finish(&self) -> i32 { if !self.user_context.run_hook { return 0; @@ -159,6 +174,7 @@ where pre_render: None, post_render: None, finish: None, + exec_panic: None, } } @@ -209,4 +225,10 @@ where let _ = self.finish.insert(handler); self } + + /// Sets the handler for the `exec_panic` event. + pub fn on_exec_panic(mut self, handler: fn(&ProgramPanic)) -> Self { + let _ = self.exec_panic.insert(handler); + self + } } -- cgit