Skip to main content

dyn_quantity/quantity/
uom_impl.rs

1/*!
2This module offers a [`TryFrom`] implementation to convert from a [`DynQuantity`]
3to the statically-typed [`Quantity`] struct from the [`uom`] crate. This is a
4fallible operation, since the unit of measurement of [`DynQuantity`] is only
5known at runtime, while the unit of measurement of [`Quantity`] is defined
6at compile time via the type system.
7
8In constrast, conversion from a [`Quantity`] to a [`DynQuantity`] is infallible
9and is therefore realized via a [`From`] implementation.
10*/
11
12use num::{Zero, complex::Complex};
13use uom::si::*;
14
15use super::{DynQuantity, F64RealOrComplex};
16use crate::error::{ConversionError, NotConvertibleFromComplexF64};
17use crate::unit::Unit;
18
19#[cfg(feature = "uom")]
20impl<L, M, T, I, Th, N, J, K> crate::unit::UnitFromType
21    for uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>
22where
23    L: uom::typenum::Integer,
24    M: uom::typenum::Integer,
25    T: uom::typenum::Integer,
26    I: uom::typenum::Integer,
27    Th: uom::typenum::Integer,
28    N: uom::typenum::Integer,
29    J: uom::typenum::Integer,
30    K: ?Sized,
31{
32    fn unit_from_type() -> Unit {
33        return Unit {
34            second: T::to_i32(),
35            meter: L::to_i32(),
36            kilogram: M::to_i32(),
37            ampere: I::to_i32(),
38            kelvin: Th::to_i32(),
39            mol: N::to_i32(),
40            candela: J::to_i32(),
41        };
42    }
43}
44
45impl<L, M, T, I, Th, N, J, K, V> TryFrom<DynQuantity<V>>
46    for uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>
47where
48    L: uom::typenum::Integer,
49    M: uom::typenum::Integer,
50    T: uom::typenum::Integer,
51    I: uom::typenum::Integer,
52    Th: uom::typenum::Integer,
53    N: uom::typenum::Integer,
54    J: uom::typenum::Integer,
55    K: ?Sized,
56    V: F64RealOrComplex,
57{
58    type Error = ConversionError;
59
60    fn try_from(quantity: DynQuantity<V>) -> Result<Self, Self::Error> {
61        // Check dimensional correctness (compare runtime to compile-time unit exponents)
62        let expected = Unit {
63            second: T::to_i32(),
64            meter: L::to_i32(),
65            kilogram: M::to_i32(),
66            ampere: I::to_i32(),
67            kelvin: Th::to_i32(),
68            mol: N::to_i32(),
69            candela: J::to_i32(),
70        };
71        if expected != quantity.unit {
72            return Err(ConversionError::UnitMismatch {
73                expected,
74                found: quantity.unit,
75            });
76        }
77
78        // Construct the uom quantity directly from raw data. This is feasible since si_value() converts the quantity value to a coherent SI value
79        // (e.g by converting 1 km into 1000 m)
80        let value = quantity.value.to_complexf64();
81
82        // Return an error if the value contains a complex component
83        if !value.im.is_zero() {
84            return Err(ConversionError::NotConvertibleFromComplexF64(
85                NotConvertibleFromComplexF64 {
86                    source: value,
87                    target_type: "f64",
88                },
89            ));
90        }
91
92        return Ok(uom::si::Quantity {
93            dimension: std::marker::PhantomData,
94            units: std::marker::PhantomData,
95            value: value.re,
96        });
97    }
98}
99
100impl<L, M, T, I, Th, N, J, K, V> TryFrom<DynQuantity<V>>
101    for uom::si::Quantity<
102        uom::si::ISQ<L, M, T, I, Th, N, J, K>,
103        uom::si::SI<Complex<f64>>,
104        Complex<f64>,
105    >
106where
107    L: uom::typenum::Integer,
108    M: uom::typenum::Integer,
109    T: uom::typenum::Integer,
110    I: uom::typenum::Integer,
111    Th: uom::typenum::Integer,
112    N: uom::typenum::Integer,
113    J: uom::typenum::Integer,
114    K: ?Sized,
115    V: F64RealOrComplex,
116{
117    type Error = ConversionError;
118
119    fn try_from(quantity: DynQuantity<V>) -> Result<Self, Self::Error> {
120        // Check dimensional correctness (compare runtime to compile-time unit exponents)
121        let expected = Unit {
122            second: T::to_i32(),
123            meter: L::to_i32(),
124            kilogram: M::to_i32(),
125            ampere: I::to_i32(),
126            kelvin: Th::to_i32(),
127            mol: N::to_i32(),
128            candela: J::to_i32(),
129        };
130        if expected != quantity.unit {
131            return Err(ConversionError::UnitMismatch {
132                expected,
133                found: quantity.unit,
134            });
135        }
136
137        // Construct the uom quantity directly from raw data. This is feasible
138        // since value converts the quantity value to a coherent SI value
139        // (e.g by converting 1 km into 1000 m)
140        let value = quantity.value.to_complexf64();
141
142        return Ok(uom::si::Quantity {
143            dimension: std::marker::PhantomData,
144            units: std::marker::PhantomData,
145            value,
146        });
147    }
148}
149
150impl<L, M, T, I, Th, N, J, K, V>
151    From<uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>>
152    for DynQuantity<V>
153where
154    L: uom::typenum::Integer,
155    M: uom::typenum::Integer,
156    T: uom::typenum::Integer,
157    I: uom::typenum::Integer,
158    Th: uom::typenum::Integer,
159    N: uom::typenum::Integer,
160    J: uom::typenum::Integer,
161    K: ?Sized,
162    V: F64RealOrComplex,
163{
164    fn from(
165        quantitiy: uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>,
166    ) -> Self {
167        let exponents = Unit {
168            second: T::to_i32(),
169            meter: L::to_i32(),
170            kilogram: M::to_i32(),
171            ampere: I::to_i32(),
172            kelvin: Th::to_i32(),
173            mol: N::to_i32(),
174            candela: J::to_i32(),
175        };
176        return DynQuantity::new(V::from_f64(quantitiy.value), exponents);
177    }
178}
179
180impl<L, M, T, I, Th, N, J, K, V>
181    TryFrom<
182        uom::si::Quantity<
183            uom::si::ISQ<L, M, T, I, Th, N, J, K>,
184            uom::si::SI<Complex<f64>>,
185            Complex<f64>,
186        >,
187    > for DynQuantity<V>
188where
189    L: uom::typenum::Integer,
190    M: uom::typenum::Integer,
191    T: uom::typenum::Integer,
192    I: uom::typenum::Integer,
193    Th: uom::typenum::Integer,
194    N: uom::typenum::Integer,
195    J: uom::typenum::Integer,
196    K: ?Sized,
197    V: F64RealOrComplex,
198{
199    type Error = NotConvertibleFromComplexF64;
200
201    fn try_from(
202        quantitiy: uom::si::Quantity<
203            uom::si::ISQ<L, M, T, I, Th, N, J, K>,
204            uom::si::SI<Complex<f64>>,
205            Complex<f64>,
206        >,
207    ) -> Result<Self, Self::Error> {
208        let value = V::try_from_complexf64(quantitiy.value)?;
209
210        let exponents = Unit {
211            second: T::to_i32(),
212            meter: L::to_i32(),
213            kilogram: M::to_i32(),
214            ampere: I::to_i32(),
215            kelvin: Th::to_i32(),
216            mol: N::to_i32(),
217            candela: J::to_i32(),
218        };
219        return Ok(DynQuantity::new(value, exponents));
220    }
221}