Skip to main content

carapace_wasm/input_v1/
error.rs

1//! `DecodeError` — decode's own diagnostic type, in `AnalysisError`'s style
2//! (tagged variants carrying context, not
3//! sentinel codes. This is defense in depth against what `pysees`'s
4//! compiler can't see (engine/schema version skew, a stale snapshot run
5//! against a newer/older wasm build) — the compiler is expected to catch
6//! entity-level problems (missing tags, unsupported materials, ...) first.
7
8use carapace_core::model::ModelError;
9use serde::Serialize;
10
11/// `Serialize`, not `Deserialize` — a `DecodeError` only ever flows *out*
12/// to JS (`boundary.rs`), as a `{ kind: "...", ... }`-shaped object.
13#[derive(Debug, Clone, PartialEq, Serialize, tsify::Tsify)]
14#[serde(
15    tag = "kind",
16    rename_all = "camelCase",
17    rename_all_fields = "camelCase"
18)]
19pub enum DecodeError {
20    /// An analysis parameter is non-finite or outside its valid range.
21    InvalidAnalysisOption {
22        stage: String,
23        field: &'static str,
24    },
25    /// `header.ndm` is neither 2 nor 3.
26    UnsupportedNdm {
27        got: u8,
28    },
29    /// A table that belongs to the other profile (a 3D beam-column table in a
30    /// 2D model, or the reverse) is not empty.
31    TableNotInProfile {
32        table: &'static str,
33    },
34    /// A reference (element load, recorder) names an element kind that
35    /// belongs to the other profile.
36    ElementKindNotInProfile {
37        table: &'static str,
38    },
39    /// A recorder's `component` is past the element's local force width (or
40    /// the load width, for an `elementLoad` recorder). `recorder` is its
41    /// index in `sequence.recorders`.
42    InvalidRecorderComponent {
43        recorder: u32,
44        component: u8,
45        width: u32,
46    },
47    /// A Gauss-point recorder names a point the element does not have (`count`
48    /// is the element's Gauss-point count, zero for a non-continuum kind).
49    InvalidGaussPoint {
50        recorder: u32,
51        point: u32,
52        count: u32,
53    },
54    /// A plane material's constants are unusable.
55    InvalidPlaneMaterial {
56        index: u32,
57        reason: &'static str,
58    },
59    /// A shell section's constants are unusable.
60    InvalidShellSection {
61        index: u32,
62        reason: &'static str,
63    },
64    /// A row's shape is wrong for the profile or its sparse encoding (a
65    /// friction row with the wrong number of shear DOFs, a 2D `orient` row
66    /// with `yp`, `fibers.z` not parallel to `y`, malformed offsets, ...).
67    InvalidRow {
68        table: &'static str,
69        row: u32,
70        reason: &'static str,
71    },
72    UnknownNodeIndex {
73        table: &'static str,
74        row: u32,
75    },
76    UnknownMaterialIndex {
77        table: &'static str,
78        row: u32,
79    },
80    CyclicMaterialReference {
81        index: u32,
82    },
83    UnknownPatternIndex {
84        table: &'static str,
85        row: u32,
86    },
87    UnknownFiberSectionIndex {
88        row: u32,
89    },
90    UnknownElementIndex {
91        table: &'static str,
92        row: u32,
93    },
94    UnknownStageIndex {
95        table: &'static str,
96        row: u32,
97    },
98    /// An element load referenced an element kind `core` doesn't apply
99    /// that load to (today, only the beam-column kinds honor
100    /// `Uniform`).
101    UnsupportedElementLoad {
102        element_kind: &'static str,
103    },
104    InvalidDof {
105        table: &'static str,
106        row: u32,
107        dof: u8,
108    },
109    /// A sparse per-row entry (`EqualDofTable::dofs`/`RigidDiaphragmTable::
110    /// constrained`) named a row past the
111    /// end of that table's own dense `retained` list.
112    UnknownConstraintRow {
113        table: &'static str,
114        row: u32,
115    },
116    /// A zero-length element's `orient` vectors are zero or parallel, or (for
117    /// a 2D element) leave the xy plane, so no local frame exists.
118    InvalidOrientation {
119        table: &'static str,
120        row: u32,
121    },
122    /// The assembled model failed `core`'s `Domain::validate` (a nodal load
123    /// on a DOF nothing uses, an element rejecting its own geometry, ...).
124    InvalidModel {
125        error: ModelErrorDetail,
126    },
127}
128
129/// `core::ModelError`, restated for the wire. Node and element indices are
130/// the rows of the node table and of the element tables in insertion order
131/// (all element kinds in decode order), so a caller can map them back.
132#[derive(Debug, Clone, Copy, PartialEq, Serialize, tsify::Tsify)]
133#[serde(
134    tag = "kind",
135    rename_all = "camelCase",
136    rename_all_fields = "camelCase"
137)]
138pub enum ModelErrorDetail {
139    LoadOnInactiveDof {
140        node: usize,
141        dof: usize,
142    },
143    DuplicateSlave {
144        node: usize,
145        dof: usize,
146    },
147    SlaveIsFixed {
148        node: usize,
149        dof: usize,
150    },
151    ConstraintCycle {
152        node: usize,
153        dof: usize,
154    },
155    ConstraintOnPrescribedDof {
156        node: usize,
157        dof: usize,
158    },
159    InconsistentInitialState {
160        node: usize,
161        dof: usize,
162    },
163    MassOnConstrainedDof {
164        node: usize,
165        dof: usize,
166    },
167    IncompatibleElementLoad {
168        element: usize,
169    },
170    InvalidElement {
171        element: usize,
172        reason: &'static str,
173    },
174}
175
176impl From<ModelError> for ModelErrorDetail {
177    fn from(error: ModelError) -> Self {
178        match error {
179            ModelError::LoadOnInactiveDof { node, dof } => {
180                ModelErrorDetail::LoadOnInactiveDof { node, dof }
181            }
182            ModelError::DuplicateSlave { node, dof } => {
183                ModelErrorDetail::DuplicateSlave { node, dof }
184            }
185            ModelError::SlaveIsFixed { node, dof } => ModelErrorDetail::SlaveIsFixed { node, dof },
186            ModelError::ConstraintCycle { node, dof } => {
187                ModelErrorDetail::ConstraintCycle { node, dof }
188            }
189            ModelError::ConstraintOnPrescribedDof { node, dof } => {
190                ModelErrorDetail::ConstraintOnPrescribedDof { node, dof }
191            }
192            ModelError::InconsistentInitialState { node, dof } => {
193                ModelErrorDetail::InconsistentInitialState { node, dof }
194            }
195            ModelError::MassOnConstrainedDof { node, dof } => {
196                ModelErrorDetail::MassOnConstrainedDof { node, dof }
197            }
198            ModelError::IncompatibleElementLoad { element } => {
199                ModelErrorDetail::IncompatibleElementLoad { element }
200            }
201            ModelError::InvalidElement { element, reason } => {
202                ModelErrorDetail::InvalidElement { element, reason }
203            }
204        }
205    }
206}