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 {}