num_valid/core/
errors.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! Error types for validation and conversion failures.
4//!
5//! This module provides the error types used throughout the library for reporting
6//! validation failures, conversion errors, and function domain violations.
7
8use crate::kernels::RawRealTrait;
9use num::Integer;
10use std::{backtrace::Backtrace, fmt::Display};
11use thiserror::Error;
12
13//--------------------------------------------
14// Errors for raw real number validation
15//--------------------------------------------
16/// Errors that can occur during the validation of a raw real number.
17///
18/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
19/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
20#[derive(Debug, Error)]
21pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
22    /// The value is subnormal.
23    #[error("Value is subnormal: {value}")]
24    IsSubnormal {
25        /// The subnormal value that failed validation.
26        value: RawReal,
27
28        /// A captured backtrace for debugging purposes.
29        backtrace: Backtrace,
30    },
31
32    /// The value is NaN (Not a Number).
33    #[error("Value is NaN: {value}")]
34    IsNaN {
35        /// The NaN value that failed validation.
36        value: RawReal,
37
38        /// A captured backtrace for debugging purposes.
39        backtrace: Backtrace,
40    },
41
42    /// The value is positive infinity.
43    #[error("Value is positive infinity")]
44    IsPosInfinity {
45        /// The positive infinity value that failed validation.
46        backtrace: Backtrace,
47    },
48
49    /// The value is negative infinity.
50    #[error("Value is negative infinity")]
51    IsNegInfinity {
52        /// The negative infinity value that failed validation.
53        backtrace: Backtrace,
54    },
55
56    /// The precision of the input does not match the requested precision.
57    #[error(
58        "precision mismatch: input real has precision {actual_precision}, \
59        but the requested precision is {requested_precision}"
60    )]
61    PrecisionMismatch {
62        /// The input value.
63        input_value: RawReal,
64
65        /// The precision of the input value.
66        actual_precision: u32,
67
68        /// The requested precision.
69        requested_precision: u32,
70
71        /// A captured backtrace for debugging purposes.
72        backtrace: Backtrace,
73    },
74    /*
75    // The following variants are placeholders for potential future validation policies
76    // and are not currently used by StrictFinitePolicy.
77
78    #[error("Value is not strictly positive: {value}")]
79    NotPositive {
80        value: RawReal,
81        backtrace: Backtrace,
82    },
83
84    #[error("Value is negative: {value}")]
85    Negative {
86        value: RawReal,
87        backtrace: Backtrace,
88    },
89
90    #[error("Value is zero: {value}")]
91    IsZero {
92        value: RawReal,
93        backtrace: Backtrace,
94    },
95
96    #[error("Value {value} is outside the allowed range [{min}, {max}]")]
97    OutOfRange {
98        value: RawReal,
99        min: RawReal,
100        max: RawReal,
101        backtrace: Backtrace,
102    },
103    */
104}
105
106//--------------------------------------------
107// Errors for complex number validation
108//--------------------------------------------
109/// Errors that can occur during the validation of a complex number.
110///
111/// This enum is generic over `ErrorValidationReal`, which is the error type returned
112/// by validating the real and imaginary parts of the complex number (e.g.,
113/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
114///
115/// It indicates whether the real part, the imaginary part, or both parts failed validation.
116#[derive(Debug, Error)]
117pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
118    /// The real part of the complex number failed validation.
119    #[error("Real part validation error: {source}")]
120    InvalidRealPart {
121        /// The underlying validation error for the real part.
122        #[source]
123        #[backtrace]
124        source: ErrorValidationReal,
125    },
126
127    /// The imaginary part of the complex number failed validation.
128    #[error("Imaginary part validation error: {source}")]
129    InvalidImaginaryPart {
130        /// The underlying validation error for the imaginary part.
131        #[source]
132        #[backtrace]
133        source: ErrorValidationReal,
134    },
135
136    /// Both the real and imaginary parts of the complex number failed validation.
137    #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
138    InvalidBothParts {
139        /// The validation error for the real part.
140        real_error: ErrorValidationReal,
141
142        /// The validation error for the imaginary part.
143        imag_error: ErrorValidationReal,
144    },
145}
146
147//-------------------------------------------------------------
148/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
149///
150/// This enum distinguishes between:
151/// 1.  Failures due to the `f64` not being exactly representable at the target `PRECISION`
152///     of the `RawReal` type.
153/// 2.  General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
154#[derive(Debug, Error)]
155pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
156    /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
157    #[error(
158        "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
159    )]
160    NonRepresentableExactly {
161        /// The `f64` value that could not be exactly represented.
162        value_in: f64,
163
164        /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
165        value_converted_from_f64: RawReal,
166
167        /// The precision that was requested.
168        precision: u32,
169
170        /// A captured backtrace for debugging purposes.
171        backtrace: Backtrace,
172    },
173
174    /// The output value failed validation.
175    #[error("the output value is invalid!")]
176    Output {
177        /// The underlying validation error for the `output` value.
178        #[from]
179        source: ErrorsValidationRawReal<RawReal>,
180    },
181}
182//-------------------------------------------------------------
183
184//-------------------------------------------------------------
185/// Errors that can occur when converting a raw real number to an integer type.
186///
187/// This enum represents the possible failures when attempting to convert a
188/// floating-point number to an integer, including non-finite values, non-integer
189/// values (with fractional parts), and values outside the representable range
190/// of the target integer type.
191///
192/// # Type Parameters
193///
194/// - `RawReal`: The source floating-point type implementing [`RawRealTrait`].
195/// - `IntType`: The target integer type implementing [`Integer`].
196#[derive(Debug, Error)]
197pub enum ErrorsRawRealToInteger<RawReal: RawRealTrait, IntType: Integer> {
198    /// The `RawReal` value is not finite (i.e., it is NaN or Infinity).
199    #[error("Value is not finite: {value}")]
200    NotFinite {
201        /// The non-finite `RawReal` value that caused the error.
202        value: RawReal,
203
204        /// A captured backtrace for debugging purposes.
205        backtrace: Backtrace,
206    },
207
208    /// The `RawReal` value is not an integer (i.e., it has a fractional part).
209    #[error("Value is not an integer: {value}")]
210    NotAnInteger {
211        /// The `RawReal` value that is not an integer.
212        value: RawReal,
213
214        /// A captured backtrace for debugging purposes.
215        backtrace: Backtrace,
216    },
217
218    /// The `RawReal` value is outside the range representable by the target integer type `IntType`.
219    #[error("Value {value} is out of range [{min}, {max}] for target integer type")]
220    OutOfRange {
221        /// The `RawReal` value that is out of range.
222        value: RawReal,
223
224        /// The minimum representable value of the target integer type `IntType`.
225        min: IntType,
226
227        /// The maximum representable value of the target integer type `IntType`.
228        max: IntType,
229
230        /// A captured backtrace for debugging purposes.
231        backtrace: Backtrace,
232    },
233}
234//-------------------------------------------------------------
235
236//--------------------------------------------------------------------------------------------------
237// Conditional Backtrace Support
238//--------------------------------------------------------------------------------------------------
239
240/// Captures a backtrace if the `backtrace` feature is enabled.
241///
242/// When the `backtrace` feature is enabled, this function captures a full backtrace
243/// using [`capture_backtrace()`]. When disabled (default), it returns a
244/// disabled backtrace with zero overhead.
245///
246/// # Performance
247///
248/// Backtrace capture is expensive (~1-10μs per capture). For performance-critical
249/// code, leave the `backtrace` feature disabled. Enable it during development or
250/// debugging to get detailed error traces.
251///
252/// # Example
253///
254/// ```toml
255/// # In Cargo.toml, enable backtraces for debugging:
256/// [dependencies]
257/// num-valid = { version = "0.2", features = ["backtrace"] }
258/// ```
259#[inline(always)]
260pub fn capture_backtrace() -> Backtrace {
261    #[cfg(feature = "backtrace")]
262    {
263        Backtrace::force_capture()
264    }
265    #[cfg(not(feature = "backtrace"))]
266    {
267        Backtrace::disabled()
268    }
269}
270
271/// Returns `true` if the `backtrace` feature is enabled.
272///
273/// This can be used to conditionally check whether backtraces are being captured.
274///
275/// # Example
276///
277/// ```rust
278/// use num_valid::core::errors::is_backtrace_enabled;
279///
280/// if is_backtrace_enabled() {
281///     println!("Backtraces are enabled - errors will include stack traces");
282/// } else {
283///     println!("Backtraces are disabled for performance");
284/// }
285/// ```
286#[inline(always)]
287pub const fn is_backtrace_enabled() -> bool {
288    #[cfg(feature = "backtrace")]
289    {
290        true
291    }
292    #[cfg(not(feature = "backtrace"))]
293    {
294        false
295    }
296}
297
298//--------------------------------------------------------------------------------------------------