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}