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}