Skip to main content

dyn_quantity/
error.rs

1/*!
2This module contains the various errors which can occur when dealing with
3[`DynQuantity`](crate::DynQuantity) and [`Unit`].
4*/
5
6use std::error::Error;
7use std::fmt::Display;
8
9use num::Complex;
10
11use crate::Unit;
12
13/**
14Error representing unequality of units.
15
16Sometimes, units of measurements must be identical for a certain operation. For
17example, two physical quantities can only be added if their units are
18identical. This struct holds both involved units for further inspection.
19 */
20#[derive(Debug, Clone, PartialEq, Default)]
21#[repr(C)]
22pub struct UnitsNotEqual(pub Unit, pub Unit);
23
24impl Display for UnitsNotEqual {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "unit {} not equal to unit {}", self.0, self.1)
27    }
28}
29
30impl Error for UnitsNotEqual {}
31
32/**
33Error representing a failed attempt to calculate the `n`th root of an [`Unit`].
34
35Calculating the `n`th root of an [`Unit`] fails if any of the exponents
36is not divisible by `n`.
37 */
38#[derive(Default, Debug, Clone, PartialEq)]
39pub struct RootError {
40    /// Root index which lead to the error.
41    pub n: i32,
42    /// Exponents for which the `n`th root could not be calculated.
43    pub unit: Unit,
44}
45
46impl std::fmt::Display for RootError {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(
49            f,
50            "not possible to calculate the {}th root (exponents {} cannot be divided by {} without remainder)",
51            &self.n, &self.unit, &self.n
52        )
53    }
54}
55
56impl std::error::Error for RootError {}
57
58/**
59Error representing a failed attempt to parse a string into a
60[`DynQuantity`](crate::quantity::DynQuantity).
61 */
62#[derive(Default, Debug, Clone, PartialEq)]
63pub struct ParseError {
64    /**
65    String which could not be parsed
66     */
67    pub substring: String,
68    /**
69    The span can be used to index into the string to get the exact characters
70    which could not be parsed.
71     */
72    pub span: std::ops::Range<usize>,
73    /**
74    Parsing can fail due to a variety of reasons, see the docstring of [`ParseErrorReason`].
75     */
76    pub reason: ParseErrorReason,
77}
78
79impl std::fmt::Display for ParseError {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        write!(f, "could not parse {}: {}", &self.substring, &self.reason)
82    }
83}
84
85/**
86The varying reasons parsing a string to a
87[`DynQuantity`](crate::quantity::DynQuantity) can fail.
88This struct is part of [`ParseError`], which contains the information where
89the parsing failed.
90 */
91#[derive(Default, Debug, Clone, PartialEq)]
92#[repr(u8)]
93pub enum ParseErrorReason {
94    /// String contained an unexpected token.
95    UnexpectedToken,
96    /// Input string was empty.
97    InputIsEmpty,
98    /// Brackets in the string are not balanced:
99    /// - "((5)": Closing bracket is missing
100    /// - "(5))": Opening bracket is missing
101    UnbalancedBrackets,
102    /// Two numbers without any combining operator are in the string:
103    /// - "5 32": Invalid because it is unclear how the numbers should
104    /// combined in the resulting [`DynQuantity`](crate::quantity::DynQuantity).
105    /// - "5 * 32": Valid
106    TwoInnersWithoutOperator,
107    /// Two operators without a number inbetween are in the string:
108    /// - "3 / * 2": Invalid
109    /// - "3 / 1 * 2": Valid
110    TwoOperatorsWithoutInner,
111    /// The string must not start with certain characters, for example:
112    /// - Operators such as *, /, ^
113    /// - Closing brackets
114    MustNotStartWith,
115    /**
116    An addition / subtraction of two invalid quantities was defined in the
117    string, e.g. "3 A + 2 V".
118     */
119    UnitsNotEqual(UnitsNotEqual),
120    /// See docstring of [`NotConvertibleFromComplexF64`].
121    NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
122    /// Generic fallback error for all other parsing failures
123    #[default]
124    CouldNotParse,
125}
126
127impl std::fmt::Display for ParseErrorReason {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        match self {
130            ParseErrorReason::UnexpectedToken => {
131                write!(f, "unexpected token")
132            }
133            ParseErrorReason::InputIsEmpty => {
134                write!(f, "input is empty")
135            }
136            ParseErrorReason::CouldNotParse => write!(f, "could not parse the input"),
137            ParseErrorReason::UnbalancedBrackets => {
138                write!(f, "unbalanced number of brackets")
139            }
140            ParseErrorReason::TwoInnersWithoutOperator => {
141                write!(
142                    f,
143                    "encountered two numbers without an operator (+ or -) between them"
144                )
145            }
146            ParseErrorReason::TwoOperatorsWithoutInner => {
147                write!(
148                    f,
149                    "encountered two operators (+, -, * or /) without a number between them"
150                )
151            }
152            ParseErrorReason::UnitsNotEqual(inner) => inner.fmt(f),
153            ParseErrorReason::MustNotStartWith => {
154                write!(f, "input must not start with this token")
155            }
156            ParseErrorReason::NotConvertibleFromComplexF64(err) => err.fmt(f),
157        }
158    }
159}
160
161impl From<UnitsNotEqual> for ParseErrorReason {
162    fn from(value: UnitsNotEqual) -> Self {
163        return Self::UnitsNotEqual(value);
164    }
165}
166
167impl std::error::Error for ParseErrorReason {}
168
169/**
170Error describing a failed attempt to convert a [`Complex<f64>`] into the type
171`V` of [`DynQuantity<V>`](crate::quantity::DynQuantity).
172
173For example, this error will be returned when trying to parse a string
174representing a complex quantity into a
175[`DynQuantity<f64>`](crate::quantity::DynQuantity).
176 */
177#[derive(Debug, Clone, PartialEq)]
178pub struct NotConvertibleFromComplexF64 {
179    /// Inner which failed to convert.
180    pub source: Complex<f64>,
181    /// Target type name.
182    pub target_type: &'static str,
183}
184
185impl std::fmt::Display for NotConvertibleFromComplexF64 {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        write!(
188            f,
189            "could not convert from {} into target type {}",
190            self.source, self.target_type
191        )
192    }
193}
194
195impl std::error::Error for NotConvertibleFromComplexF64 {}
196
197/**
198Error describing a failed attempt to convert between different types representing
199quantities.
200
201This error can e.g. be returned when trying to convert a
202[`DynQuantity`](crate::quantity::DynQuantity) to a
203[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) via the
204[`TryFrom`] implementation. See docstring of
205[`DynQuantity`](crate::quantity::DynQuantity).
206*/
207#[derive(Debug, Clone, PartialEq)]
208pub enum ConversionError {
209    /// See docstring of [`NotConvertibleFromComplexF64`].
210    NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
211    /// Expected a certain unit of measurement, but found a different one.
212    UnitMismatch {
213        /// Unit of measurement which was expected.
214        expected: Unit,
215        /// Unit of measurement which was found.
216        found: Unit,
217    },
218    /// Fallback case for all other errors.
219    Custom(String),
220}
221
222impl ConversionError {
223    /// Create [`Self`] from anything which can be converted to a string.
224    pub fn custom<T: ToString>(err: T) -> Self {
225        return Self::Custom(err.to_string());
226    }
227}
228
229impl std::fmt::Display for ConversionError {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        match self {
232            ConversionError::NotConvertibleFromComplexF64(value) => return value.fmt(f),
233            ConversionError::UnitMismatch { expected, found } => {
234                write!(f, "expected {}, found {}", expected, found)
235            }
236            ConversionError::Custom(string) => {
237                write!(f, "{string}")
238            }
239        }
240    }
241}
242
243impl std::error::Error for ConversionError {}