Skip to main content

scirs2_special/
error_context.rs

1//! Enhanced error handling with context tracking for special functions
2//!
3//! This module provides consistent error handling patterns across all special functions
4//! with detailed context tracking for better debugging and error recovery.
5
6use crate::error::{SpecialError, SpecialResult};
7use std::fmt;
8
9/// Context information for error tracking
10#[derive(Debug, Clone)]
11pub struct ErrorContext {
12    /// The function where the error occurred
13    pub function_name: String,
14    /// The specific operation that failed
15    pub operation: String,
16    /// Input parameters that caused the error
17    pub parameters: Vec<(String, String)>,
18    /// Additional context information
19    pub additional_info: Option<String>,
20}
21
22impl ErrorContext {
23    /// Create a new error context
24    pub fn new(function_name: impl Into<String>, operation: impl Into<String>) -> Self {
25        Self {
26            function_name: function_name.into(),
27            operation: operation.into(),
28            parameters: Vec::new(),
29            additional_info: None,
30        }
31    }
32
33    /// Add a parameter to the context
34    pub fn with_param(mut self, name: impl Into<String>, value: impl fmt::Display) -> Self {
35        self.parameters.push((name.into(), value.to_string()));
36        self
37    }
38
39    /// Add additional information
40    pub fn with_info(mut self, info: impl Into<String>) -> Self {
41        self.additional_info = Some(info.into());
42        self
43    }
44
45    /// Convert to a formatted error message
46    pub fn to_error_message(&self) -> String {
47        let mut msg = format!("Error in {} during {}", self.function_name, self.operation);
48
49        if !self.parameters.is_empty() {
50            msg.push_str(" with parameters: ");
51            let params: Vec<String> = self
52                .parameters
53                .iter()
54                .map(|(name, value)| format!("{name}={value}"))
55                .collect();
56            msg.push_str(&params.join(", "));
57        }
58
59        if let Some(ref info) = self.additional_info {
60            msg.push_str(&format!(". {info}"));
61        }
62
63        msg
64    }
65}
66
67/// Enhanced error type with context
68#[derive(Debug)]
69pub struct ContextualError {
70    pub error: SpecialError,
71    pub context: ErrorContext,
72}
73
74impl fmt::Display for ContextualError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}: {}", self.context.to_error_message(), self.error)
77    }
78}
79
80impl std::error::Error for ContextualError {}
81
82/// Extension trait for adding context to errors
83pub trait ErrorContextExt<T> {
84    /// Add context to an error
85    fn with_context<F>(self, f: F) -> SpecialResult<T>
86    where
87        F: FnOnce() -> ErrorContext;
88}
89
90impl<T> ErrorContextExt<T> for SpecialResult<T> {
91    fn with_context<F>(self, f: F) -> SpecialResult<T>
92    where
93        F: FnOnce() -> ErrorContext,
94    {
95        self.map_err(|e| {
96            let ctx = f();
97            SpecialError::ComputationError(format!("{}: {e}", ctx.to_error_message()))
98        })
99    }
100}
101
102/// Standard validation trait for special functions
103pub trait ValidatedFunction<Input, Output> {
104    /// Validate inputs before computation
105    fn validateinputs(&self, input: &Input) -> SpecialResult<()>;
106
107    /// Compute the function with validated inputs
108    fn compute_validated(&self, input: Input) -> SpecialResult<Output>;
109
110    /// Main entry point that combines validation and computation
111    fn evaluate(&self, input: Input) -> SpecialResult<Output> {
112        self.validateinputs(&input)?;
113        self.compute_validated(input)
114    }
115}
116
117/// Error recovery strategies
118#[derive(Debug, Clone, Copy)]
119pub enum RecoveryStrategy {
120    /// Return a default value
121    ReturnDefault,
122    /// Clamp to valid range
123    ClampToRange,
124    /// Use approximation
125    UseApproximation,
126    /// Propagate error
127    PropagateError,
128}
129
130/// Error recovery trait
131pub trait ErrorRecovery<T> {
132    /// Attempt to recover from an error
133    fn recover(&self, error: &SpecialError, strategy: RecoveryStrategy) -> Option<T>;
134}
135
136/// Macro for consistent error creation with context
137#[macro_export]
138macro_rules! special_error {
139    (domain: $func:expr, $op:expr, $($param:expr => $value:expr),* $(,)?) => {{
140        let mut ctx = $crate::error_context::ErrorContext::new($func, $op);
141        $(ctx = ctx.with_param($param, $value);)*
142        $crate::error::SpecialError::DomainError(ctx.to_error_message())
143    }};
144
145    (convergence: $func:expr, $op:expr, $($param:expr => $value:expr),* $(,)?) => {{
146        let mut ctx = $crate::error_context::ErrorContext::new($func, $op);
147        $(ctx = ctx.with_param($param, $value);)*
148        $crate::error::SpecialError::ConvergenceError(ctx.to_error_message())
149    }};
150
151    (computation: $func:expr, $op:expr, $($param:expr => $value:expr),* $(,)?) => {{
152        let mut ctx = $crate::error_context::ErrorContext::new($func, $op);
153        $(ctx = ctx.with_param($param, $value);)*
154        $crate::error::SpecialError::ComputationError(ctx.to_error_message())
155    }};
156}
157
158/// Macro for consistent validation with error context
159#[macro_export]
160macro_rules! validate_with_context {
161    ($condition:expr, $error_type:ident, $func:expr, $msg:expr $(, $param:expr => $value:expr)*) => {
162        if !($condition) {
163            let mut ctx = $crate::error_context::ErrorContext::new($func, "validation");
164            $(ctx = ctx.with_param($param, $value);)*
165            ctx = ctx.with_info($msg);
166            return Err($crate::error::SpecialError::$error_type(ctx.to_error_message()));
167        }
168    };
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn test_error_context() {
177        let ctx = ErrorContext::new("gamma", "computation")
178            .with_param("x", -1.0)
179            .with_info("Gamma function is undefined at negative integers");
180
181        let msg = ctx.to_error_message();
182        assert!(msg.contains("gamma"));
183        assert!(msg.contains("x=-1"));
184        assert!(msg.contains("undefined"));
185    }
186
187    #[test]
188    fn test_error_context_macro() {
189        let err = special_error!(
190            domain: "bessel_j", "evaluation",
191            "n" => 5,
192            "x" => -10.0
193        );
194
195        match err {
196            SpecialError::DomainError(msg) => {
197                assert!(msg.contains("bessel_j"));
198                assert!(msg.contains("n=5"));
199                assert!(msg.contains("x=-10"));
200            }
201            _ => panic!("Wrong error type"),
202        }
203    }
204}