Skip to main content

energyml_geometry/
error.rs

1//! What can go wrong between an object and a set of points.
2
3use std::fmt;
4
5/// Result of a geometry operation.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Something that stopped a representation from becoming points.
9#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12    /// A shape the caller asked for that this object cannot take.
13    ///
14    /// Not a gap in this build: the file states no connection the answer could
15    /// be derived from. Names both ends, because whoever asked wrote a rule and
16    /// needs to know which of their rules met which object.
17    #[error(
18        "{topology} cannot be drawn as {wanted}: the file states no connection this could be \
19         derived from"
20    )]
21    Shape {
22        /// What the object is, in words - "a set of polylines".
23        topology: &'static str,
24        /// What was asked of it - "a surface".
25        wanted: &'static str,
26    },
27
28    /// A form of array this crate does not evaluate yet.
29    ///
30    /// **Named, never silent.** The alternative - returning no points - looks
31    /// exactly like a working program with an empty file, and that is the
32    /// failure mode this crate exists to avoid.
33    #[error("`{form}` is not evaluated yet ({at})")]
34    Unsupported {
35        /// The concrete type met, e.g. `Point3dParametricArray`.
36        form: &'static str,
37        /// Where it was met, as a dotted path.
38        at: String,
39    },
40
41    /// The file states a shape with **no extent of its own**.
42    ///
43    /// Different news from [`Error::Unsupported`], and the difference is one a
44    /// caller acts on. *Unsupported* means this build does not evaluate that
45    /// form yet, so waiting is a strategy. This means the file **never says**
46    /// how far the shape goes: a `HorizontalPlaneGeometry` is one number, a
47    /// depth, and a plane is infinite. No release will add the extent, because
48    /// it is not a fact about the model - it is a decision about the picture,
49    /// and it belongs to whoever is drawing.
50    ///
51    /// So the refusal names what is missing rather than pretending to a gap,
52    /// and a viewer can offer the one thing that works: an extent of its own.
53    /// Drawing some box instead would put a surface of invented size into a
54    /// model, which is the failure this crate exists to avoid.
55    #[error("`{form}` has no extent of its own ({at}): {states}")]
56    Unbounded {
57        /// The concrete type met, e.g. `HorizontalPlaneGeometry`.
58        form: &'static str,
59        /// Where it was met, as a dotted path.
60        at: String,
61        /// What the file *does* state - the half that is there.
62        states: &'static str,
63    },
64
65    /// The object is not shaped the way its schema says.
66    #[error("{at}: {message}")]
67    Malformed {
68        /// Where, as a dotted path.
69        at: String,
70        /// What was expected and what was found.
71        message: String,
72    },
73
74    /// The referenced object is not in the store.
75    #[error("`{0}` is not in this store")]
76    Missing(String),
77
78    /// Reading an external array failed.
79    #[error(transparent)]
80    Storage(#[from] energyml_storage::Error),
81
82    /// Reading or walking an object failed.
83    #[error(transparent)]
84    Core(#[from] energyml_core::error::Error),
85
86    /// A unit, a coordinate system or a reprojection could not be read.
87    #[error(transparent)]
88    Domain(#[from] energyml_domain::Error),
89}
90
91impl Error {
92    /// Whether this is "the external file is not there".
93    ///
94    /// A package whose `.h5` companion was not shipped is a gap in what the
95    /// caller was given, not a defect in the model - and a viewer says so
96    /// differently from "this build cannot evaluate that form".
97    pub fn is_missing_file(&self) -> bool {
98        match self {
99            Error::Storage(energyml_storage::Error::ExternalData {
100                reason: energyml_storage::ExternalDataReason::FileMissing,
101                ..
102            }) => true,
103            Error::Domain(e) => e.is_missing_file(),
104            _ => false,
105        }
106    }
107
108    /// A shape the caller asked for that this object cannot take.
109    ///
110    /// Separate from [`Error::Unsupported`] on purpose, and the distinction is
111    /// the whole message: that one means *this build does not do it yet*, and
112    /// invites a future version. This one means *the file does not say enough
113    /// for anyone to do it* - a set of polylines is not a surface in any build.
114    pub fn shape(topology: &'static str, wanted: &'static str) -> Self {
115        Self::Shape { topology, wanted }
116    }
117
118    /// A form this crate meets but does not evaluate.
119    pub fn unsupported(form: &'static str, at: impl fmt::Display) -> Self {
120        Self::Unsupported {
121            form,
122            at: at.to_string(),
123        }
124    }
125
126    /// A shape whose extent the file does not state.
127    pub fn unbounded(form: &'static str, at: impl fmt::Display, states: &'static str) -> Self {
128        Self::Unbounded {
129            form,
130            at: at.to_string(),
131            states,
132        }
133    }
134
135    /// An object that does not match its schema.
136    pub fn malformed(at: impl fmt::Display, message: impl fmt::Display) -> Self {
137        Self::Malformed {
138            at: at.to_string(),
139            message: message.to_string(),
140        }
141    }
142}