Skip to main content

dace_rs/
error.rs

1//! Error type for DACE operations.
2//!
3//! DACE's C core uses a sticky thread-local error code (`XYY`: severity `X`,
4//! number `YY`) that is polled by the caller, and calls `exit(1)` on internal
5//! panic-level failures. `dace-rs` instead:
6//!
7//! - reports recoverable failures (initialization, parsing, blob import) as
8//!   [`Result::Err`] holding a [`DaceError`] carrying the same numeric `XYY`
9//!   code as the C library;
10//! - **panics** with a `DaceError` payload for numeric/domain misuse during
11//!   operations (division by zero, logarithm of a non-positive DA, ...), since
12//!   the operator traits (`Mul`, `Div`, ...) cannot return `Result`;
13//! - downgrades the C library's purely informational messages (severity
14//!   `INFO`/`WARNING`) to [`log`] records with documented degraded behavior,
15//!   mirroring the C library instead of failing.
16//!
17//! There is no sticky error state and no `exit`.
18
19use std::fmt;
20
21/// A DACE error: a numeric `XYY` code (severity `X` in `{1,3,6,9,10}`, number
22/// `YY`) plus a human-readable message, matching the C library's error table.
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct DaceError {
25    /// Numeric `XYY` error code, identical to the C library's `daceGetError`.
26    pub code: u32,
27    /// Human-readable error message.
28    pub message: String,
29}
30
31impl DaceError {
32    /// Create a new error with the given code and message.
33    pub fn new(code: u32, message: impl Into<String>) -> Self {
34        DaceError {
35            code,
36            message: message.into(),
37        }
38    }
39
40    /// Severity digit `X` of the `XYY` code (1 = info, 3 = warning,
41    /// 6 = error, 9 = severe, 10 = panic).
42    pub fn severity(&self) -> u32 {
43        self.code / 100
44    }
45}
46
47impl fmt::Display for DaceError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "DACE error {}: {}", self.code, self.message)
50    }
51}
52
53impl std::error::Error for DaceError {}
54
55/// Panic with a `DaceError` payload (numeric/domain misuse during operations).
56pub(crate) fn dace_panic(code: u32, message: &str) -> ! {
57    std::panic::panic_any(DaceError::new(code, message))
58}
59
60/// Error codes reused across the crate (from the C library's `DACEerr` table).
61// Error codes reused across the crate; some are only used by later-phase
62// modules (elementary/special functions), so unused variants are allowed.
63#[allow(dead_code)]
64pub(crate) mod codes {
65    pub const NOT_INITIALIZED: u32 = 1003;
66    pub const ORDER_VARIABLE_TOO_LARGE: u32 = 911;
67    pub const ORDER_TOO_LARGE: u32 = 622;
68    pub const INVALID_ENCODED_EXPONENT: u32 = 626;
69    pub const DIVIDING_BY_ZERO: u32 = 641;
70    pub const INVERSE_DOES_NOT_EXIST: u32 = 642;
71    pub const NON_INTEGER_POWER_NON_POSITIVE: u32 = 643;
72    pub const ZERO_TH_ROOT: u32 = 644;
73    pub const EVEN_ROOT_NEGATIVE: u32 = 645;
74    pub const ODD_ROOT_ZERO: u32 = 646;
75    pub const LOG_NON_POSITIVE: u32 = 647;
76    pub const LOG_BASE_POSITIVE: u32 = 648;
77    pub const COS_ZERO_IN_TANGENT: u32 = 649;
78    pub const TOO_MANY_VARIABLES: u32 = 165;
79    pub const OUT_OF_DOMAIN: u32 = 650;
80}