dyn_quantity/lib.rs
1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4mod unit_exponents_constructors;
5
6use num::Complex;
7use num::complex::ComplexFloat;
8
9use std::error::Error;
10use std::fmt::Display;
11use std::ops::{Div, DivAssign, Mul, MulAssign};
12pub use std::str::FromStr;
13
14#[cfg(feature = "uom")]
15pub mod uom_impl;
16
17#[cfg(feature = "serde")]
18pub mod serde_impl;
19
20#[cfg(feature = "serde")]
21pub mod deserialize_with;
22
23#[cfg(feature = "serde")]
24pub use deserialize_with::{
25 deserialize_angle, deserialize_opt_angle, deserialize_opt_quantity,
26 deserialize_opt_vec_of_quantities, deserialize_quantity, deserialize_vec_of_quantities,
27};
28
29#[cfg(feature = "from_str")]
30pub mod from_str;
31
32/**
33A trait to derive [`UnitExponents`] from a type. This trait bridges the gap
34between (external) types representing physical quantities (such as e.g. the
35[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) type from
36the [uom](https://crates.io/crates/uom) crate) and [`UnitExponents`].
37 */
38pub trait AsUnitExponents {
39 /**
40 This function derives an [`UnitExponents`] from any type which implements
41 [`AsUnitExponents`]. Its default implementation returns an [`UnitExponents`]
42 where all exponents are zero.
43
44 # Examples
45 ```
46 use dyn_quantity::AsUnitExponents;
47 use uom::si::f64::Length;
48
49 // 64-bit floats do not represent a physical quantity
50 let exp = f64::as_unit_exponents();
51 assert_eq!(exp.meter, 0);
52
53 // The "Length" type alias from the uom crate represents a physical quantity (length)
54 let exp = Length::as_unit_exponents();
55 assert_eq!(exp.meter, 1);
56 ```
57 */
58 fn as_unit_exponents() -> UnitExponents {
59 return UnitExponents::default();
60 }
61}
62
63impl AsUnitExponents for f64 {}
64
65impl AsUnitExponents for Complex<f64> {}
66
67mod private {
68 use super::Complex;
69
70 pub trait Sealed {}
71
72 impl Sealed for f64 {}
73 impl Sealed for Complex<f64> {}
74}
75
76/**
77This is an internal trait which is used to convert between [`f64`] and
78[`Complex<f64>`]-based [`DynQuantity`] structs. It needs to be public because
79it is part of the type signature of [`DynQuantity`], but it is not meant to be
80implemented by external types and is therefore sealed.
81*/
82pub trait F64RealOrComplex:
83 ComplexFloat<Real: std::fmt::Display>
84 + std::ops::AddAssign
85 + std::ops::SubAssign
86 + std::fmt::Display
87 + std::ops::MulAssign
88 + std::ops::DivAssign
89 + std::fmt::Debug
90 + private::Sealed
91{
92 /**
93 Tries to convert from a [`Complex<f64>`] to the implementor of this trait,
94 either [`Complex<f64>`] or [`f64`]. The former conversion always succeeds
95 (is a no-op), while the latter fails if `number` has an imaginary component.
96 */
97 fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64>;
98
99 /**
100 Converts a [`Complex<f64>`] or [`f64`] to a [`Complex<f64>`]. This is
101 infallible (and a no-op in case of the former conversion).
102 */
103 fn to_complexf64(self) -> Complex<f64>;
104
105 /**
106 Converts a [`f64`] to a [`Complex<f64>`] or [`f64`]. This is infallible
107 (and a no-op in case of the latter conversion).
108 */
109 fn from_f64(value: f64) -> Self;
110
111 /**
112 Sets the real part of the implementor to `value`.
113 */
114 fn set_re_f64(&mut self, value: f64) -> ();
115
116 /**
117 Sets the imaginary part of the implementor to `value`. If the implementing
118 type is [`f64`], this is a no-op.
119 */
120 fn set_im_f64(&mut self, value: f64) -> ();
121
122 /**
123 Calcute the `n`th root of `self`.
124 */
125 fn nth_root(self, n: i32) -> Self;
126}
127
128impl F64RealOrComplex for f64 {
129 fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
130 if number.im() == 0.0 {
131 return Ok(number.re());
132 } else {
133 return Err(NotConvertibleFromComplexF64 {
134 source: number,
135 target_type: "f64",
136 });
137 }
138 }
139
140 fn to_complexf64(self) -> Complex<f64> {
141 return Complex::new(self, 0.0);
142 }
143
144 fn from_f64(value: f64) -> Self {
145 return value;
146 }
147
148 fn set_re_f64(&mut self, value: f64) -> () {
149 *self = value;
150 }
151
152 fn set_im_f64(&mut self, _: f64) -> () {
153 ();
154 }
155
156 fn nth_root(self, n: i32) -> Self {
157 return self.powf(1.0 / n as f64);
158 }
159}
160
161impl F64RealOrComplex for Complex<f64> {
162 fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
163 return Ok(Complex::<f64>::from(number));
164 }
165
166 fn to_complexf64(self) -> Complex<f64> {
167 return self;
168 }
169
170 fn from_f64(value: f64) -> Self {
171 return Complex::new(value, 0.0);
172 }
173
174 fn set_re_f64(&mut self, value: f64) -> () {
175 self.re = value;
176 }
177
178 fn set_im_f64(&mut self, value: f64) -> () {
179 self.im = value;
180 }
181
182 fn nth_root(self, n: i32) -> Self {
183 return self.powf(1.0 / n as f64);
184 }
185}
186
187/**
188This type represents a physical quantity via its numerical `value` and a unit of
189measurement (field `exponents`). The unit of measurement is not defined via the
190type system, but rather via the values of the [`UnitExponents`]. This means that
191the unit of measurement is not fixed at compile time, but can change dynamically
192at runtime.
193
194This property is very useful when e.g. parsing a user-provided string to a
195physical quantity. For this case, [`DynQuantity`] implements the
196[`FromStr`](`std::str::FromStr`) trait. The module documentation
197[`from_str`](crate::from_str) has more information regarding the available syntax.
198
199The `V` generic parameter needs to implement [`F64RealOrComplex`].
200Currently, implementors are [`f64`] and [`Complex<f64>`]. It is possible to
201convert between those two types using the methods provided by [`F64RealOrComplex`].
202In general, you likely want `V` to be [`f64`] except when dealing with complex
203quantities such as alternating currents.
204
205This struct can also be pretty-print the quantity it represents via its
206[`std::fmt::Display`] implementation:
207
208```
209use std::str::FromStr;
210use dyn_quantity::DynQuantity;
211
212let quantity = DynQuantity::<f64>::from_str("9.81 m/s^2").expect("parseable");
213assert_eq!(quantity.to_string(), "9.81 s^-2 m".to_string());
214```
215
216# Conversion into uom `Quantity`
217
218If the **uom** feature is enabled, a [`DynQuantity`] can be (fallible)
219converted into a
220[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) via
221[`TryFrom`]. In combination with the aforementioned parsing capabilities, this
222allows fallible parsing of strings to statically-typed physical quantities.
223
224# Serialization and deserialization
225
226If the **serde** feature is enabled, this struct can be serialized and
227deserialized. Serialization creates the "standard"
228[serde](https://crates.io/crates/serde) representation one would expect from the
229[`Serialize`] macro. When deserializing however, multiple options are available:
2301) Using the "standard" serialized representation of a struct. For example, the
231yaml representation of a [`DynQuantity<f64>`] looks like this:
232```text
233---
234value: 2.0
235exponents:
236 second: 0
237 meter: 1
238 kilogram: 0
239 ampere: 1
240 kelvin: 0
241 mol: 0
242 candela: 0
243```
244
2452) Deserializing directly from a string. This uses the [`std::str::FromStr`]
246implementation under the hood, see the [`from_str`](crate::from_str) module
247documentation. Only available if the **from_str** feature is enabled.
2483) Deserialize directly from a real or complex value. This option is mainly here
249to allow deserializing a serialized [uom](https://crates.io/crates/uom) quantity
250(whose serialized representation is simply its numerical value without any
251units). For example, deserializing `5.0` into [`DynQuantity<f64>`] produces the
252same result as deserializing:
253```text
254---
255value: 5.0
256exponents:
257 second: 0
258 meter: 0
259 kilogram: 0
260 ampere: 0
261 kelvin: 0
262 mol: 0
263 candela: 0
264```
265
266The three different possibilities are realized via a crate-internal untagged enum.
267*/
268#[derive(Debug, Clone, PartialEq, Default)]
269#[repr(C)]
270pub struct DynQuantity<V: F64RealOrComplex> {
271 /**
272 The value of the physical quantity.
273 */
274 pub value: V,
275 /**
276 The (SI) base units of the physical quantity, represented by their exponents.
277 */
278 pub exponents: UnitExponents,
279}
280
281impl<V: F64RealOrComplex> DynQuantity<V> {
282 /**
283 Returns a new instance of `Self`.
284 */
285 pub fn new(value: V, exponents: UnitExponents) -> Self {
286 return Self { value, exponents };
287 }
288
289 /**
290 Fallible addition of `self` and `other`.
291
292 Physical quantities can only be added together if their units are identical.
293 Hence, this function first compares the `.exponents` fields of `self` and
294 `other`. If they are identical, the `value` fields are added up and the
295 resulting quantity is returned. Otherwise, a [`UnitsOfSummandsNotIdentical`]
296 error is returned.
297
298 # Examples
299 ```
300 use std::str::FromStr;
301 use dyn_quantity::DynQuantity;
302
303 let curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
304 let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
305 let volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
306
307 // The currents can be added ...
308 let curr_sum = curr1.try_add(&curr2).expect("can be added");
309 assert_eq!(curr_sum.value, 2.0);
310 assert_eq!(curr_sum.exponents.ampere, 1);
311
312 // ... but adding a current to a voltage fails.
313 assert!(volt1.try_add(&curr1).is_err());
314 ```
315 */
316 pub fn try_add(&self, other: &Self) -> Result<Self, UnitsOfSummandsNotIdentical> {
317 let mut output = self.clone();
318 output.try_add_assign(other)?;
319 return Ok(output);
320 }
321
322 /**
323 Like [`DynQuantity::try_add`], but assigns the sum of `self` and `other` to
324 `self` instead of returning it. If the addition fails, `self` is not modified.
325
326 # Examples
327 ```
328 use std::str::FromStr;
329 use dyn_quantity::DynQuantity;
330
331 let mut curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
332 let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
333 let mut volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
334
335 // curr1 gets overwritten
336 curr1.try_add_assign(&curr2).expect("can be added");
337 assert_eq!(curr1.value, 2.0);
338 assert_eq!(curr1.exponents.ampere, 1);
339
340 // volt1 does not get modified because the addition failed
341 let volt_cpy = volt1.clone();
342 assert!(volt1.try_add_assign(&curr1).is_err());
343 assert_eq!(volt1, volt_cpy);
344 ```
345 */
346 pub fn try_add_assign(&mut self, other: &Self) -> Result<(), UnitsOfSummandsNotIdentical> {
347 if self.exponents == other.exponents {
348 self.value += other.value;
349 return Ok(());
350 } else {
351 return Err(UnitsOfSummandsNotIdentical(
352 self.exponents.clone(),
353 other.exponents.clone(),
354 ));
355 }
356 }
357
358 /**
359 Fallible subtraction of `self` and `other`.
360
361 Physical quantities can only be subtracted from each other if their units are identical.
362 Hence, this function first compares the `.exponents` fields of `self` and
363 `other`. If they are identical, the `value` fields are subtracted up and the
364 resulting quantity is returned. Otherwise, a [`UnitsOfSummandsNotIdentical`]
365 error is returned.
366
367 # Examples
368 ```
369 use std::str::FromStr;
370 use dyn_quantity::DynQuantity;
371
372 let curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
373 let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
374 let volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
375
376 // The currents can be subtracted ...
377 let curr_sum = curr1.try_sub(&curr2).expect("can be added");
378 assert_eq!(curr_sum.value, 0.0);
379 assert_eq!(curr_sum.exponents.ampere, 1);
380
381 // ... but sbutracting a current from a voltage fails.
382 assert!(volt1.try_sub(&curr1).is_err());
383 ```
384 */
385 pub fn try_sub(&self, other: &Self) -> Result<Self, UnitsOfSummandsNotIdentical> {
386 let mut output = self.clone();
387 output.try_sub_assign(other)?;
388 return Ok(output);
389 }
390
391 /**
392 Like [`DynQuantity::try_sub`], but assigns the difference of `self` and `other` to
393 `self` instead of returning it. If the subtraction fails, `self` is not modified.
394
395 # Examples
396 ```
397 use std::str::FromStr;
398 use dyn_quantity::DynQuantity;
399
400 let mut curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
401 let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
402 let mut volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
403
404 // curr1 gets overwritten
405 curr1.try_sub_assign(&curr2).expect("can be added");
406 assert_eq!(curr1.value, 0.0);
407 assert_eq!(curr1.exponents.ampere, 1);
408
409 // volt1 does not get modified because the addition failed
410 let volt_cpy = volt1.clone();
411 assert!(volt1.try_sub_assign(&curr1).is_err());
412 assert_eq!(volt1, volt_cpy);
413 ```
414 */
415 pub fn try_sub_assign(&mut self, other: &Self) -> Result<(), UnitsOfSummandsNotIdentical> {
416 if self.exponents == other.exponents {
417 self.value -= other.value;
418 return Ok(());
419 } else {
420 return Err(UnitsOfSummandsNotIdentical(
421 self.exponents.clone(),
422 other.exponents.clone(),
423 ));
424 }
425 }
426
427 /**
428 Raises `self` to an integer power.
429
430 # Examples
431 ```
432 use std::str::FromStr;
433 use dyn_quantity::DynQuantity;
434
435 let curr = DynQuantity::<f64>::from_str("2 A^2").expect("valid");
436 let result = curr.powi(3);
437 assert_eq!(result.value, 8.0);
438 assert_eq!(result.exponents.ampere, 6);
439 ```
440 */
441 pub fn powi(mut self, n: i32) -> Self {
442 self.value = self.value.powi(n);
443 self.exponents = self.exponents.powi(n);
444 return self;
445 }
446
447 /**
448 Tries to calculate the `n`th root of self.
449 */
450 pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
451 self.exponents = self.exponents.try_nthroot(n)?;
452 self.value = self.value.nth_root(n);
453 return Ok(self);
454 }
455}
456
457impl<V: F64RealOrComplex> std::fmt::Display for DynQuantity<V> {
458 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459 if self.value.im() == V::zero().im() {
460 write!(f, "{}", self.value.re())?;
461 } else {
462 write!(f, "({})", self.value)?;
463 }
464
465 // Go through all units and add them, if their exponents aren't zero
466 if self.exponents.second != 0 {
467 if self.exponents.second == 1 {
468 write!(f, " s")?;
469 } else {
470 write!(f, " s^{}", self.exponents.second)?;
471 }
472 }
473 if self.exponents.meter != 0 {
474 if self.exponents.meter == 1 {
475 write!(f, " m")?;
476 } else {
477 write!(f, " m^{}", self.exponents.meter)?;
478 }
479 }
480 if self.exponents.kilogram != 0 {
481 if self.exponents.kilogram == 1 {
482 write!(f, " kg")?;
483 } else {
484 write!(f, " kg^{}", self.exponents.kilogram)?;
485 }
486 }
487 if self.exponents.ampere != 0 {
488 if self.exponents.ampere == 1 {
489 write!(f, " A")?;
490 } else {
491 write!(f, " A^{}", self.exponents.ampere)?;
492 }
493 }
494 if self.exponents.kelvin != 0 {
495 if self.exponents.kelvin == 1 {
496 write!(f, " K")?;
497 } else {
498 write!(f, " K^{}", self.exponents.kelvin)?;
499 }
500 }
501 if self.exponents.mol != 0 {
502 if self.exponents.mol == 1 {
503 write!(f, " mol")?;
504 } else {
505 write!(f, " mol^{}", self.exponents.mol)?;
506 }
507 }
508 if self.exponents.candela != 0 {
509 if self.exponents.candela == 1 {
510 write!(f, " cd")?;
511 } else {
512 write!(f, " cd^{}", self.exponents.candela)?;
513 }
514 }
515
516 return Ok(());
517 }
518}
519
520impl<V: F64RealOrComplex> Mul for DynQuantity<V> {
521 type Output = Self;
522
523 fn mul(mut self, rhs: Self) -> Self::Output {
524 self.mul_assign(rhs);
525 return self;
526 }
527}
528
529impl<V: F64RealOrComplex> MulAssign for DynQuantity<V> {
530 fn mul_assign(&mut self, rhs: Self) {
531 self.value *= rhs.value;
532 self.exponents *= rhs.exponents;
533 }
534}
535
536impl<V: F64RealOrComplex> Div for DynQuantity<V> {
537 type Output = Self;
538
539 fn div(mut self, rhs: Self) -> Self::Output {
540 self.div_assign(rhs);
541 return self;
542 }
543}
544
545impl<V: F64RealOrComplex> DivAssign for DynQuantity<V> {
546 fn div_assign(&mut self, rhs: Self) {
547 if self.value.is_infinite() {
548 let mut value = self.value / rhs.value;
549 if value.re().is_nan() {
550 value.set_re_f64(0.0);
551 }
552 if value.im().is_nan() {
553 value.set_im_f64(0.0);
554 }
555 self.value = value;
556 } else {
557 self.value /= rhs.value;
558 }
559 self.exponents /= rhs.exponents;
560 }
561}
562
563impl TryFrom<DynQuantity<Complex<f64>>> for DynQuantity<f64> {
564 type Error = NotConvertibleFromComplexF64;
565
566 fn try_from(quantity: DynQuantity<Complex<f64>>) -> Result<Self, Self::Error> {
567 if quantity.value.im() == 0.0 {
568 return Ok(DynQuantity::new(quantity.value.re(), quantity.exponents));
569 } else {
570 return Err(NotConvertibleFromComplexF64 {
571 source: quantity.value,
572 target_type: "f64",
573 });
574 }
575 }
576}
577
578// ========================================================
579
580/**
581Converts a slice of [`DynQuantity`] to a vector of their values `V`. In
582constrast to [`to_vec_checked`], this function does not check whether the units
583of the individual [`DynQuantity`] elements are identical.
584*/
585pub fn to_vec<V: F64RealOrComplex>(quantity_slice: &[DynQuantity<V>]) -> Vec<V> {
586 let mut output: Vec<V> = Vec::with_capacity(quantity_slice.len());
587 for element in quantity_slice.iter() {
588 output.push(element.value)
589 }
590 return output;
591}
592
593/**
594Checks if all units of the [`DynQuantity`] elements are identical. If that is
595the case, it converts the slice to a vector of their values `V`.
596*/
597pub fn to_vec_checked(quantity_slice: &[DynQuantity<f64>]) -> Result<Vec<f64>, ConversionError> {
598 let mut output: Vec<f64> = Vec::with_capacity(quantity_slice.len());
599 if let Some(first_element) = quantity_slice.first() {
600 let exponents = first_element.exponents.clone();
601 for element in quantity_slice.iter() {
602 if element.exponents != exponents {
603 return Err(ConversionError::UnitMismatch {
604 expected: exponents,
605 found: element.exponents.clone(),
606 });
607 }
608 output.push(element.value)
609 }
610 }
611 return Ok(output);
612}
613
614// ====================================================
615
616/**
617Struct representing a unit of measurement in the SI system via the exponents of
618the base units.
619 */
620#[derive(Debug, Clone, PartialEq, Eq, Default)]
621#[repr(C)]
622pub struct UnitExponents {
623 /// Exponent for the SI base unit of time.
624 pub second: i32,
625 /// Exponent for the SI base unit of length.
626 pub meter: i32,
627 /// Exponent for the SI base unit of mass.
628 pub kilogram: i32,
629 /// Exponent for the SI base unit of electrical current.
630 pub ampere: i32,
631 /// Exponent for the SI base unit of temperature.
632 pub kelvin: i32,
633 /// Exponent for the SI base unit of amount of substance.
634 pub mol: i32,
635 /// Exponent for the SI base unit of luminous intensity
636 pub candela: i32,
637}
638
639impl From<[i32; 7]> for UnitExponents {
640 /**
641 Converts an array of seven `i32` values into `UnitExponents`.
642
643 The individual array elements are interpreted as follows:
644 - `array[0]`: Exponent of second
645 - `array[1]`: Exponent of meter
646 - `array[2]`: Exponent of kilogram
647 - `array[3]`: Exponent of ampere
648 - `array[4]`: Exponent of kelvin
649 - `array[5]`: Exponent of mol
650 - `array[6]`: Exponent of candela
651 */
652 fn from(array: [i32; 7]) -> Self {
653 return UnitExponents {
654 second: array[0],
655 meter: array[1],
656 kilogram: array[2],
657 ampere: array[3],
658 kelvin: array[4],
659 mol: array[5],
660 candela: array[6],
661 };
662 }
663}
664
665impl From<UnitExponents> for [i32; 7] {
666 /**
667 Converts an `UnitExponents` into an array of seven `i32`.
668
669 The exponents are put into the array in the following order:
670 - `array[0]`: Exponent of second
671 - `array[1]`: Exponent of meter
672 - `array[2]`: Exponent of kilogram
673 - `array[3]`: Exponent of ampere
674 - `array[4]`: Exponent of kelvin
675 - `array[5]`: Exponent of mol
676 - `array[6]`: Exponent of candela
677 */
678 fn from(value: UnitExponents) -> Self {
679 return [
680 value.second,
681 value.meter,
682 value.kilogram,
683 value.ampere,
684 value.kelvin,
685 value.mol,
686 value.candela,
687 ];
688 }
689}
690
691impl UnitExponents {
692 /**
693 Raises `self` to an integer power.
694
695 # Examples
696 ```
697 use dyn_quantity::UnitExponents;
698
699 let exponents = UnitExponents::from([0, 1, 0, 2, 0, -2, 0]);
700 let array: [i32; 7] = exponents.powi(2).into();
701 assert_eq!(array, [0, 2, 0, 4, 0, -4, 0]);
702 ```
703 */
704 pub fn powi(mut self, n: i32) -> Self {
705 self.second *= n;
706 self.meter *= n;
707 self.kilogram *= n;
708 self.ampere *= n;
709 self.kelvin *= n;
710 self.mol *= n;
711 self.candela *= n;
712 return self;
713 }
714
715 /**
716 Tries to calculate the `n`th root of self. This operation fails if any
717 of the exponents is not divisible by `n`.
718
719 # Examples
720 ```
721 use dyn_quantity::UnitExponents;
722
723 let exponents = UnitExponents::from([0, 2, 0, 2, 0, -4, 0]);
724
725 // It is possible to calculate the square root:
726 let array: [i32; 7] = exponents.clone().try_nthroot(2).unwrap().into();
727 assert_eq!(array, [0, 1, 0, 1, 0, -2, 0]);
728
729 // But not the cubic root (not all exponents are divisible by 3):
730 assert!(exponents.try_nthroot(3).is_err());
731 ```
732 */
733 pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
734 fn try_nthroot_inner(
735 exponents: &UnitExponents,
736 exp: i32,
737 n: i32,
738 ) -> Result<i32, RootError> {
739 if exp % n == 0 {
740 return Ok(exp / n);
741 } else {
742 return Err(RootError {
743 n,
744 exponents: exponents.clone(),
745 });
746 }
747 }
748 let init_exp = self.clone();
749 self.second = try_nthroot_inner(&init_exp, self.second, n)?;
750 self.meter = try_nthroot_inner(&init_exp, self.meter, n)?;
751 self.kilogram = try_nthroot_inner(&init_exp, self.kilogram, n)?;
752 self.ampere = try_nthroot_inner(&init_exp, self.ampere, n)?;
753 self.kelvin = try_nthroot_inner(&init_exp, self.kelvin, n)?;
754 self.mol = try_nthroot_inner(&init_exp, self.mol, n)?;
755 self.candela = try_nthroot_inner(&init_exp, self.candela, n)?;
756 return Ok(self);
757 }
758}
759
760impl std::fmt::Display for UnitExponents {
761 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762 write!(
763 f,
764 "s^{} m^{} kg^{} A^{} K^{} mol^{} cd^{}",
765 self.second,
766 self.meter,
767 self.kilogram,
768 self.ampere,
769 self.kelvin,
770 self.mol,
771 self.candela
772 )
773 }
774}
775
776impl Mul for UnitExponents {
777 type Output = Self;
778
779 fn mul(mut self, rhs: Self) -> Self::Output {
780 self.mul_assign(rhs);
781 return self;
782 }
783}
784
785impl MulAssign for UnitExponents {
786 fn mul_assign(&mut self, rhs: Self) {
787 self.second += rhs.second;
788 self.meter += rhs.meter;
789 self.kilogram += rhs.kilogram;
790 self.ampere += rhs.ampere;
791 self.kelvin += rhs.kelvin;
792 self.mol += rhs.mol;
793 self.candela += rhs.candela;
794 }
795}
796
797impl Div for UnitExponents {
798 type Output = Self;
799
800 fn div(mut self, rhs: Self) -> Self::Output {
801 self.div_assign(rhs);
802 return self;
803 }
804}
805
806impl DivAssign for UnitExponents {
807 fn div_assign(&mut self, rhs: Self) {
808 self.second -= rhs.second;
809 self.meter -= rhs.meter;
810 self.kilogram -= rhs.kilogram;
811 self.ampere -= rhs.ampere;
812 self.kelvin -= rhs.kelvin;
813 self.mol -= rhs.mol;
814 self.candela -= rhs.candela;
815 }
816}
817
818/**
819Error representing a failed attempt to add two physical quantities.
820
821Two physical quantities can only be added if their units of measurements are
822identical. If they aren't, the corresponding function will return an instance
823of this struct, which holds the [`UnitExponents`] of both quantities for
824further inspection.
825 */
826#[derive(Debug, Clone, PartialEq, Default)]
827#[repr(C)]
828pub struct UnitsOfSummandsNotIdentical(pub UnitExponents, pub UnitExponents);
829
830impl Display for UnitsOfSummandsNotIdentical {
831 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
832 write!(
833 f,
834 "first summand has exponents {}, but second summand has exponents {}",
835 self.0, self.1
836 )
837 }
838}
839
840impl Error for UnitsOfSummandsNotIdentical {}
841
842/**
843Error representing a failed attempt to calculate the `n`th root of a [`UnitExponents`].
844
845Calculating the `n`th root of a [`UnitExponents`] fails if any of the exponents
846is not divisible by `n`.
847 */
848#[derive(Default, Debug, Clone, PartialEq)]
849pub struct RootError {
850 /// Root index which lead to the error.
851 pub n: i32,
852 /// Exponents for which the `n`th root could not be calculated.
853 pub exponents: UnitExponents,
854}
855
856impl std::fmt::Display for RootError {
857 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
858 write!(
859 f,
860 "not possible to calculate the {}th root (exponents {} cannot be divided by {} without remainder)",
861 &self.n, &self.exponents, &self.n
862 )
863 }
864}
865
866impl std::error::Error for RootError {}
867
868/**
869Error representing a failed attempt to parse a string into a [`DynQuantity`].
870 */
871#[derive(Default, Debug, Clone, PartialEq)]
872pub struct ParseError {
873 /**
874 String which could not be parsed
875 */
876 pub substring: String,
877 /**
878 The span can be used to index into the string to get the exact characters
879 which could not be parsed.
880 */
881 pub span: std::ops::Range<usize>,
882 /**
883 Parsing can fail due to a variety of reasons, see the docstring of [`ParseErrorReason`].
884 */
885 pub reason: ParseErrorReason,
886}
887
888impl std::fmt::Display for ParseError {
889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
890 write!(f, "could not parse {}: {}", &self.substring, &self.reason)
891 }
892}
893
894/**
895The varying reasons parsing a string to a [`DynQuantity`] can fail.
896This struct is part of [`ParseError`], which contains the information where
897the parsing failed.
898 */
899#[derive(Default, Debug, Clone, PartialEq)]
900#[repr(u8)]
901pub enum ParseErrorReason {
902 /// String contained an unexpected token.
903 UnexpectedToken,
904 /// Input string was empty.
905 InputIsEmpty,
906 /// Brackets in the string are not balanced:
907 /// - "((5)": Closing bracket is missing
908 /// - "(5))": Opening bracket is missing
909 UnbalancedBrackets,
910 /// Two numbers without any combining operator are in the string:
911 /// - "5 32": Invalid because it is unclear how the numbers should
912 /// combined in the resulting [`DynQuantity`].
913 /// - "5 * 32": Valid
914 TwoNumbersWithoutOperator,
915 /// Two operators without a number inbetween are in the string:
916 /// - "3 / * 2": Invalid
917 /// - "3 / 1 * 2": Valid
918 TwoOperatorsWithoutNumber,
919 /// The string must not start with certain characters, for example:
920 /// - Operators such as *, /, ^
921 /// - Closing brackets
922 MustNotStartWith,
923 /**
924 An addition / subtraction of two invalid quantities was defined in the
925 string, e.g. "3 A + 2 V".
926 */
927 UnitsOfSummandsNotIdentical(UnitsOfSummandsNotIdentical),
928 /// See docstring of [`NotConvertibleFromComplexF64`].
929 NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
930 /// Generic fallback error for all other parsing failures
931 #[default]
932 CouldNotParse,
933}
934
935impl std::fmt::Display for ParseErrorReason {
936 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937 match self {
938 ParseErrorReason::UnexpectedToken => {
939 write!(f, "unexpected token")
940 }
941 ParseErrorReason::InputIsEmpty => {
942 write!(f, "input is empty")
943 }
944 ParseErrorReason::CouldNotParse => write!(f, "could not parse the input"),
945 ParseErrorReason::UnbalancedBrackets => {
946 write!(f, "unbalanced number of brackets")
947 }
948 ParseErrorReason::TwoNumbersWithoutOperator => {
949 write!(
950 f,
951 "encountered two numbers without an operator (+ or -) between them"
952 )
953 }
954 ParseErrorReason::TwoOperatorsWithoutNumber => {
955 write!(
956 f,
957 "encountered two operators (+, -, * or /) without a number between them"
958 )
959 }
960 ParseErrorReason::UnitsOfSummandsNotIdentical(inner) => inner.fmt(f),
961 ParseErrorReason::MustNotStartWith => {
962 write!(f, "input must not start with this token")
963 }
964 ParseErrorReason::NotConvertibleFromComplexF64(err) => err.fmt(f),
965 }
966 }
967}
968
969impl From<UnitsOfSummandsNotIdentical> for ParseErrorReason {
970 fn from(value: UnitsOfSummandsNotIdentical) -> Self {
971 return Self::UnitsOfSummandsNotIdentical(value);
972 }
973}
974
975impl std::error::Error for ParseErrorReason {}
976
977/**
978Error describing a failed attempt to convert a [`Complex<f64>`] into the type
979`V` of [`DynQuantity<V>`].
980
981For example, this error will be returned when trying to parse a string
982representing a complex quantity into a [`DynQuantity<f64>`].
983 */
984#[derive(Debug, Clone, PartialEq)]
985pub struct NotConvertibleFromComplexF64 {
986 /// Number which failed to convert.
987 pub source: Complex<f64>,
988 /// Target type name.
989 pub target_type: &'static str,
990}
991
992impl std::fmt::Display for NotConvertibleFromComplexF64 {
993 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994 write!(
995 f,
996 "could not convert from {} into target type {}",
997 self.source, self.target_type
998 )
999 }
1000}
1001
1002impl std::error::Error for NotConvertibleFromComplexF64 {}
1003
1004/**
1005Error describing a failed attempt to convert between different types representing
1006quantities.
1007
1008This error can e.g. be returned when trying to convert a [`DynQuantity`] to a
1009[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) via the
1010[`TryFrom`] implementation. See docstring of [`DynQuantity`].
1011*/
1012#[derive(Debug, Clone, PartialEq)]
1013pub enum ConversionError {
1014 /// See docstring of [`NotConvertibleFromComplexF64`].
1015 NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
1016 /// Expected a certain unit of measurement, but found a different one.
1017 UnitMismatch {
1018 /// Unit of measurement which was expected.
1019 expected: UnitExponents,
1020 /// Unit of measurement which was found.
1021 found: UnitExponents,
1022 },
1023 /// Fallback case for all other errors.
1024 Custom(String),
1025}
1026
1027impl ConversionError {
1028 /// Create [`Self`] from anything which can be converted to a string.
1029 pub fn custom<T: ToString>(err: T) -> Self {
1030 return Self::Custom(err.to_string());
1031 }
1032}
1033
1034impl std::fmt::Display for ConversionError {
1035 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1036 match self {
1037 ConversionError::NotConvertibleFromComplexF64(value) => return value.fmt(f),
1038 ConversionError::UnitMismatch { expected, found } => {
1039 write!(f, "expected {}, found {}", expected, found)
1040 }
1041 ConversionError::Custom(string) => {
1042 write!(f, "{string}")
1043 }
1044 }
1045 }
1046}
1047
1048impl std::error::Error for ConversionError {}