dyn_quantity 0.5.10

Representing physical quantities dynamically (i.e. via values, not via the type system)
Documentation
/*!
This module offers a [`TryFrom`] implementation to convert from a [`DynQuantity`]
to the statically-typed [`Quantity`] struct from the [`uom`] crate. This is a
fallible operation, since the unit of measurement of [`DynQuantity`] is only
known at runtime, while the unit of measurement of [`Quantity`] is defined
at compile time via the type system.

In constrast, conversion from a [`Quantity`] to a [`DynQuantity`] is infallible
and is therefore realized via a [`From`] implementation.
*/

use num::{Zero, complex::Complex};
use uom::si::*;

use super::{DynQuantity, F64RealOrComplex};
use crate::error::{ConversionError, NotConvertibleFromComplexF64};
use crate::unit::Unit;

#[cfg(feature = "uom")]
impl<L, M, T, I, Th, N, J, K> crate::unit::UnitFromType
    for uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
{
    fn unit_from_type() -> Unit {
        return Unit {
            second: T::to_i32(),
            meter: L::to_i32(),
            kilogram: M::to_i32(),
            ampere: I::to_i32(),
            kelvin: Th::to_i32(),
            mol: N::to_i32(),
            candela: J::to_i32(),
        };
    }
}

impl<L, M, T, I, Th, N, J, K, V> TryFrom<DynQuantity<V>>
    for uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
    V: F64RealOrComplex,
{
    type Error = ConversionError;

    fn try_from(quantity: DynQuantity<V>) -> Result<Self, Self::Error> {
        // Check dimensional correctness (compare runtime to compile-time unit
        // exponents)
        let expected = Unit {
            second: T::to_i32(),
            meter: L::to_i32(),
            kilogram: M::to_i32(),
            ampere: I::to_i32(),
            kelvin: Th::to_i32(),
            mol: N::to_i32(),
            candela: J::to_i32(),
        };
        if expected != quantity.unit {
            return Err(ConversionError::UnitMismatch {
                expected,
                found: quantity.unit,
            });
        }

        // Construct the uom quantity directly from raw data. This is feasible since
        // si_value() converts the quantity value to a coherent SI value (e.g by
        // converting 1 km into 1000 m)
        let value = quantity.value.to_complexf64();

        // Return an error if the value contains a complex component
        if !value.im.is_zero() {
            return Err(ConversionError::NotConvertibleFromComplexF64(
                NotConvertibleFromComplexF64 {
                    source: value,
                    target_type: "f64",
                },
            ));
        }

        return Ok(uom::si::Quantity {
            dimension: std::marker::PhantomData,
            units: std::marker::PhantomData,
            value: value.re,
        });
    }
}

impl<L, M, T, I, Th, N, J, K, V> TryFrom<DynQuantity<V>>
    for uom::si::Quantity<
        uom::si::ISQ<L, M, T, I, Th, N, J, K>,
        uom::si::SI<Complex<f64>>,
        Complex<f64>,
    >
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
    V: F64RealOrComplex,
{
    type Error = ConversionError;

    fn try_from(quantity: DynQuantity<V>) -> Result<Self, Self::Error> {
        // Check dimensional correctness (compare runtime to compile-time unit
        // exponents)
        let expected = Unit {
            second: T::to_i32(),
            meter: L::to_i32(),
            kilogram: M::to_i32(),
            ampere: I::to_i32(),
            kelvin: Th::to_i32(),
            mol: N::to_i32(),
            candela: J::to_i32(),
        };
        if expected != quantity.unit {
            return Err(ConversionError::UnitMismatch {
                expected,
                found: quantity.unit,
            });
        }

        // Construct the uom quantity directly from raw data. This is feasible
        // since value converts the quantity value to a coherent SI value
        // (e.g by converting 1 km into 1000 m)
        let value = quantity.value.to_complexf64();

        return Ok(uom::si::Quantity {
            dimension: std::marker::PhantomData,
            units: std::marker::PhantomData,
            value,
        });
    }
}

impl<L, M, T, I, Th, N, J, K, V>
    From<uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>>
    for DynQuantity<V>
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
    V: F64RealOrComplex,
{
    fn from(
        quantity: uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>,
    ) -> Self {
        return Self::from(&quantity);
    }
}

impl<L, M, T, I, Th, N, J, K, V>
    From<&uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>>
    for DynQuantity<V>
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
    V: F64RealOrComplex,
{
    fn from(
        quantity: &uom::si::Quantity<uom::si::ISQ<L, M, T, I, Th, N, J, K>, uom::si::SI<f64>, f64>,
    ) -> Self {
        let exponents = Unit {
            second: T::to_i32(),
            meter: L::to_i32(),
            kilogram: M::to_i32(),
            ampere: I::to_i32(),
            kelvin: Th::to_i32(),
            mol: N::to_i32(),
            candela: J::to_i32(),
        };
        return DynQuantity::new(V::from_f64(quantity.value.clone()), exponents);
    }
}

impl<L, M, T, I, Th, N, J, K, V>
    TryFrom<
        uom::si::Quantity<
            uom::si::ISQ<L, M, T, I, Th, N, J, K>,
            uom::si::SI<Complex<f64>>,
            Complex<f64>,
        >,
    > for DynQuantity<V>
where
    L: uom::typenum::Integer,
    M: uom::typenum::Integer,
    T: uom::typenum::Integer,
    I: uom::typenum::Integer,
    Th: uom::typenum::Integer,
    N: uom::typenum::Integer,
    J: uom::typenum::Integer,
    K: ?Sized,
    V: F64RealOrComplex,
{
    type Error = NotConvertibleFromComplexF64;

    fn try_from(
        quantity: uom::si::Quantity<
            uom::si::ISQ<L, M, T, I, Th, N, J, K>,
            uom::si::SI<Complex<f64>>,
            Complex<f64>,
        >,
    ) -> Result<Self, Self::Error> {
        let value = V::try_from_complexf64(quantity.value)?;

        let exponents = Unit {
            second: T::to_i32(),
            meter: L::to_i32(),
            kilogram: M::to_i32(),
            ampere: I::to_i32(),
            kelvin: Th::to_i32(),
            mol: N::to_i32(),
            candela: J::to_i32(),
        };
        return Ok(DynQuantity::new(value, exponents));
    }
}