Skip to main content

optionstratlib_math/error/
curves.rs

1/******************************************************************************
2    Author: Joaquín Béjar García
3    Email: jb@taunais.com
4    Date: 24/12/25
5******************************************************************************/
6
7//! Owned by `optionstratlib-math` (ADR-0001 D6, roadmap M1-14): `CurveError`.
8
9use crate::error::InterpolationError;
10use optionstratlib_core::error::OperationErrorKind;
11use optionstratlib_core::error::{OptionsError, PositionError};
12use thiserror::Error;
13
14/// Represents different types of errors that can occur in the `curves` module.
15///
16/// This enum categorizes errors that may be encountered when working with curve-related
17/// operations such as interpolation, construction, analysis, and other mathematical
18/// operations on curves and points.
19///
20/// # Variants
21///
22/// ## `Point2DError`
23/// Represents errors related to 2D point operations.
24///
25/// * `reason` - A static string explaining the specific point-related issue.
26///
27/// This variant is used for fundamental issues with points like invalid coordinates,
28/// missing values, or formatting problems.
29///
30/// ## `OperationError`
31/// Encapsulates general operational errors.
32///
33/// * `OperationErrorKind` - The specific kind of operation failure (see `OperationErrorKind` enum).
34///
35/// Used when an operation fails due to unsupported features or invalid parameters.
36///
37/// ## `StdError`
38/// Wraps standard errors with additional context.
39///
40/// * `reason` - A dynamic string providing detailed error information.
41///
42/// Suitable for general error cases where specialized variants don't apply.
43///
44/// ## `InterpolationError`
45/// Indicates issues during the curve interpolation process.
46///
47/// * `String` - A human-readable explanation of the interpolation failure.
48///
49/// Used when problems occur during data point interpolation or curve generation.
50///
51/// ## `ConstructionError`
52/// Represents errors during the construction of curves or related structures.
53///
54/// * `String` - A description of the construction issue.
55///
56/// Applicable when curve initialization fails due to invalid inputs, unsupported
57/// configurations, or missing required parameters.
58///
59/// ## `AnalysisError`
60/// Captures errors related to curve analysis operations.
61///
62/// * `String` - A detailed explanation of the analysis failure.
63///
64/// Used for failures in analytical methods like curve fitting, differentiation,
65/// or other mathematical operations on curves.
66///
67/// ## `MetricsError`
68/// Represents errors when calculating or processing curve metrics.
69///
70/// * `String` - An explanation of the metrics-related issue.
71///
72/// Used when metric calculations fail due to invalid inputs or computational issues.
73///
74/// # Usage
75///
76/// This error type is designed to be used throughout the `curves` module wherever
77/// operations might fail. It provides structured error information to help diagnose
78/// and handle various failure scenarios.
79///
80/// # Implementation Notes
81///
82/// The error variants are designed to provide useful context for debugging and error handling.
83/// Each variant includes specific information relevant to its error category.
84///
85/// # Examples
86///
87/// ```rust
88/// // Example of creating a construction error
89/// use optionstratlib_math::error::CurveError;
90/// let error = CurveError::ConstructionError("Insufficient points to construct curve".to_string());
91///
92/// // Example of creating a point error
93/// let point_error = CurveError::Point2DError { reason: "Point coordinates out of bounds" };
94/// ```
95#[derive(Error, Debug)]
96pub enum CurveError {
97    /// Error related to 2D point operations
98    #[error("Error: {reason}")]
99    Point2DError {
100        /// Static description of the point-related issue
101        reason: &'static str,
102    },
103
104    /// General operational error
105    #[error("Operation error: {0}")]
106    OperationError(
107        /// The specific kind of operation failure
108        OperationErrorKind,
109    ),
110
111    /// A caller-supplied parametric generator failed.
112    ///
113    /// `ConstructionMethod::Parametric` takes a closure written by the
114    /// caller, so its failure belongs to whichever layer wrote it, and the
115    /// math layer cannot name that type without depending on the layer
116    /// above. The cause therefore travels boxed: it stays reachable through
117    /// `std::error::Error::source` and downcastable to the original type,
118    /// instead of being flattened into a message at the construction
119    /// boundary (roadmap M1-10).
120    #[error("parametric generator failed: {0}")]
121    Generator(#[source] Box<dyn std::error::Error + Send + Sync>),
122
123    /// Error during curve interpolation
124    #[error("Interpolation error: {0}")]
125    InterpolationError(
126        /// Description of the interpolation issue
127        String,
128    ),
129
130    /// Error during curve or structure construction
131    #[error("Construction error: {0}")]
132    ConstructionError(
133        /// Details about the construction failure
134        String,
135    ),
136
137    /// Error during curve analysis operations
138    #[error("Analysis error: {0}")]
139    AnalysisError(
140        /// Explanation of the analysis issue
141        String,
142    ),
143
144    /// Error when calculating or processing curve metrics
145    #[error("Metrics error: {0}")]
146    MetricsError(
147        /// Description of the metrics-related issue
148        String,
149    ),
150
151    /// Error from position operations
152    #[error(transparent)]
153    Position(#[from] PositionError),
154
155    /// Error from options operations
156    #[error(transparent)]
157    Options(#[from] OptionsError),
158
159    /// Error from interpolation operations  
160    #[error("Interpolation error: {0}")]
161    InterpolationOp(String),
162}
163
164/// Provides helper methods for constructing specific variants of the `CurvesError` type.
165///
166/// These methods encapsulate common patterns of error creation, making it easier
167/// to consistently generate errors with the necessary context.
168///
169///
170/// ## Integration
171/// - These methods simplify the process of creating meaningful error objects, improving readability
172///   and maintainability of the code using the `CurvesError` type.
173/// - The constructed errors leverage the [`OperationErrorKind`]
174///   to ensure structured and detailed error categorization.
175impl CurveError {
176    /// ### `operation_not_supported`
177    /// Constructs a `CurvesError::OperationError` with an [`OperationErrorKind::NotSupported`] variant.
178    /// - **Parameters:**
179    ///   - `operation` (`&str`): The name of the operation that is not supported.
180    ///   - `reason` (`&str`): A description of why the operation is not supported.
181    /// - **Returns:**
182    ///   - A `CurvesError` containing a `NotSupported` operation error.
183    /// - **Use Cases:**
184    ///   - Invoked when a requested operation is not compatible with the current context.
185    ///   - For example, attempting an unsupported computation method on a specific curve type.
186    ///
187    #[must_use]
188    #[cold]
189    #[inline(never)]
190    pub fn operation_not_supported(operation: &str, reason: &str) -> Self {
191        CurveError::OperationError(OperationErrorKind::NotSupported {
192            operation: operation.to_string(),
193            reason: reason.to_string(),
194        })
195    }
196
197    /// ### `invalid_parameters`
198    /// Constructs a `CurvesError::OperationError` with an [`OperationErrorKind::InvalidParameters`] variant.
199    /// - **Parameters:**
200    ///   - `operation` (`&str`): The name of the operation that encountered invalid parameters.
201    ///   - `reason` (`&str`): A description of why the parameters are invalid.
202    /// - **Returns:**
203    ///   - A `CurvesError` containing an `InvalidParameters` operation error.
204    /// - **Use Cases:**
205    ///   - Used when an operation fails due to issues with the provided input.
206    ///   - For example, providing malformed or missing parameters for interpolation or curve construction.
207    ///
208    #[must_use]
209    #[cold]
210    #[inline(never)]
211    pub fn invalid_parameters(operation: &str, reason: &str) -> Self {
212        CurveError::OperationError(OperationErrorKind::InvalidParameters {
213            operation: operation.to_string(),
214            reason: reason.to_string(),
215        })
216    }
217}
218
219/// Type alias representing the result of operations related to curve calculations.
220///
221/// This type alias provides a standardized result type for functions that perform operations
222/// with mathematical curves, including interpolation, construction, analysis, and other
223/// curve-related operations.
224///
225/// # Type Parameters
226///
227/// * `T` - The success value type returned when operations complete successfully.
228///
229/// # Return Value
230///
231/// Returns either:
232/// * `Ok(T)` - The operation completed successfully with a value of type `T`.
233/// * `Err(CurveError)` - The operation failed, with a [`CurveError`] describing the specific failure.
234///
235/// # Usage
236///
237/// This result type is used throughout the curves module to provide consistent error handling
238/// for curve operations. It allows functions to return detailed error information using the
239/// [`CurveError`] enum when operations fail, while returning the expected value when successful.
240///
241pub type CurvesResult<T> = Result<T, CurveError>;
242
243impl CurveError {
244    /// Wraps a caller-supplied parametric generator failure, keeping the
245    /// original error reachable as the variant's `source`.
246    #[cold]
247    #[must_use]
248    pub fn generator<E>(error: E) -> Self
249    where
250        E: std::error::Error + Send + Sync + 'static,
251    {
252        CurveError::Generator(Box::new(error))
253    }
254}
255
256impl From<InterpolationError> for CurveError {
257    fn from(err: InterpolationError) -> Self {
258        CurveError::InterpolationOp(err.to_string())
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265    use std::error::Error;
266
267    #[test]
268    fn test_curves_error_display() {
269        let error = CurveError::Point2DError {
270            reason: "Invalid coordinates",
271        };
272        assert_eq!(error.to_string(), "Error: Invalid coordinates");
273
274        let error = CurveError::operation_not_supported("calculate", "Strategy");
275        assert_eq!(
276            error.to_string(),
277            "Operation error: Operation 'calculate' is not supported for strategy 'Strategy'"
278        );
279    }
280
281    #[test]
282    fn test_operation_not_supported() {
283        let error = CurveError::operation_not_supported("test_op", "TestStrat");
284        match error {
285            CurveError::OperationError(OperationErrorKind::NotSupported {
286                operation,
287                reason: strategy_type,
288            }) => {
289                assert_eq!(operation, "test_op");
290                assert_eq!(strategy_type, "TestStrat");
291            }
292            _ => panic!("Wrong error variant"),
293        }
294    }
295
296    #[test]
297    fn test_invalid_parameters() {
298        let error = CurveError::invalid_parameters("test_op", "invalid input");
299        match error {
300            CurveError::OperationError(OperationErrorKind::InvalidParameters {
301                operation,
302                reason,
303            }) => {
304                assert_eq!(operation, "test_op");
305                assert_eq!(reason, "invalid input");
306            }
307            _ => panic!("Wrong error variant"),
308        }
309    }
310
311    #[test]
312    fn test_error_trait_implementation() {
313        let error = CurveError::Point2DError {
314            reason: "test error",
315        };
316        let error_ref: &dyn Error = &error;
317        assert_eq!(error_ref.to_string(), "Error: test error");
318    }
319
320    #[test]
321    fn test_from_position_error() {
322        let position_error = PositionError::unsupported_operation("TestStruct", "test_op");
323        let curves_error = CurveError::from(position_error);
324
325        match curves_error {
326            CurveError::Position(_) => {
327                // Conversion successful
328            }
329            _ => panic!("Wrong error variant"),
330        }
331    }
332
333    #[test]
334    fn test_debug_implementation() {
335        let error = CurveError::Point2DError {
336            reason: "test debug",
337        };
338        assert!(format!("{error:?}").contains("test debug"));
339    }
340}
341
342#[cfg(test)]
343mod tests_extended {
344    use super::*;
345
346    #[test]
347    fn test_curves_error_construction_error() {
348        let error =
349            CurveError::ConstructionError("Invalid curve construction parameters".to_string());
350        assert_eq!(
351            format!("{error}"),
352            "Construction error: Invalid curve construction parameters"
353        );
354    }
355
356    #[test]
357    fn test_curves_error_analysis_error() {
358        let error =
359            CurveError::AnalysisError("Analysis failed due to insufficient data".to_string());
360        assert_eq!(
361            format!("{error}"),
362            "Analysis error: Analysis failed due to insufficient data"
363        );
364    }
365}