Skip to main content

ferritin_core/columns/
error.rs

1//! Error type with stable numeric codes for FFI and wasm-bindgen consumers.
2
3use crate::model::ModelError;
4use std::fmt;
5
6/// Category of a [`BoundaryError`].
7///
8/// The discriminants are part of the boundary contract: foreign callers may
9/// switch on [`BoundaryError::code`] and the values will not be renumbered.
10#[repr(u32)]
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub enum BoundaryErrorKind {
13    /// The model or hierarchy violates a structural invariant.
14    InvalidModel = 1,
15    /// A handle or row index is outside the exported table.
16    IndexOutOfRange = 2,
17    /// A frame was paired with a topology snapshot of a different hierarchy.
18    StaleTopology = 3,
19    /// A count or byte length does not fit the boundary's `u32` index type.
20    CapacityExceeded = 4,
21}
22
23/// An error raised while exporting columns across the WASM/FFI/GPU boundary.
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct BoundaryError {
26    kind: BoundaryErrorKind,
27    message: String,
28}
29
30impl BoundaryError {
31    pub(crate) fn new(kind: BoundaryErrorKind, message: impl Into<String>) -> Self {
32        Self {
33            kind,
34            message: message.into(),
35        }
36    }
37
38    pub(crate) fn out_of_range(what: &str, index: u32, len: u32) -> Self {
39        Self::new(
40            BoundaryErrorKind::IndexOutOfRange,
41            format!("{what} {index} is out of range (len={len})"),
42        )
43    }
44
45    /// The error category.
46    pub fn kind(&self) -> BoundaryErrorKind {
47        self.kind
48    }
49
50    /// Stable numeric code for the error category (see [`BoundaryErrorKind`]).
51    pub fn code(&self) -> u32 {
52        self.kind as u32
53    }
54
55    /// Human-readable description.
56    pub fn message(&self) -> &str {
57        &self.message
58    }
59}
60
61impl fmt::Display for BoundaryError {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.write_str(&self.message)
64    }
65}
66
67impl std::error::Error for BoundaryError {}
68
69impl From<ModelError> for BoundaryError {
70    fn from(err: ModelError) -> Self {
71        Self::new(BoundaryErrorKind::InvalidModel, err.to_string())
72    }
73}
74
75/// Convert a count to the boundary's `u32` index type.
76pub(crate) fn to_u32(what: &str, value: usize) -> Result<u32, BoundaryError> {
77    u32::try_from(value).map_err(|_| {
78        BoundaryError::new(
79            BoundaryErrorKind::CapacityExceeded,
80            format!("{what} ({value}) exceeds u32::MAX"),
81        )
82    })
83}