msla_format 0.2.0

Library for encoding and decoding various MSLA file formats: Elegoo (.goo), Chitu Encrypted (.ctb), NanoDLP (.nanodlp).
Documentation
use std::{marker::PhantomData, ops};

use crate::common::units::{LengthUnit, Meter, TimeUnit, defs::Second};

impl<L: LengthUnit> Length<L> {
    pub fn get<T: LengthUnit>(&self) -> f32 {
        T::apply(L::apply(self.value as _, 1), -1) as _
    }

    pub fn convert<T: LengthUnit>(&self) -> Length<T> {
        Length::new(self.get::<T>())
    }

    pub fn with<T: LengthUnit>(&mut self, callback: impl FnOnce(&mut f32)) {
        let mut value = self.convert::<T>();
        callback(value.raw_mut());
        *self = value.convert();
    }
}

impl<L: TimeUnit> Time<L> {
    pub fn get<T: TimeUnit>(&self) -> f32 {
        T::apply(L::apply(self.value as _, 1), -1) as _
    }

    pub fn convert<T: TimeUnit>(&self) -> Time<T> {
        Time::new(self.get::<T>())
    }
}

impl<L: LengthUnit, T: TimeUnit> Velocity<L, T> {
    pub fn get<L2: LengthUnit, T2: TimeUnit>(&self) -> f32 {
        let base = T::apply(L::apply(self.value as _, 1), -1);
        T2::apply(L2::apply(base, -1), 1) as _
    }

    pub fn convert<L2: LengthUnit, T2: TimeUnit>(&self) -> Velocity<L2, T2> {
        Velocity::new(self.get::<L2, T2>())
    }

    pub fn with<L2: LengthUnit, T2: TimeUnit>(&mut self, callback: impl FnOnce(&mut f32)) {
        let mut value = self.convert::<L2, T2>();
        callback(value.raw_mut());
        *self = value.convert();
    }
}

impl<L: LengthUnit> Area<L> {
    pub fn get<T: LengthUnit>(&self) -> f32 {
        T::apply(L::apply(self.value as _, 2), -2) as _
    }

    pub fn convert<T: LengthUnit>(&self) -> Area<T> {
        Area::new(self.get::<T>())
    }
}

impl<L: LengthUnit> Volume<L> {
    pub fn get<T: LengthUnit>(&self) -> f32 {
        T::apply(L::apply(self.value as _, 3), -3) as _
    }

    pub fn convert<T: LengthUnit>(&self) -> Volume<T> {
        Volume::new(self.get::<T>())
    }
}

impl<T1: TimeUnit, T2: TimeUnit> ops::Add<Time<T2>> for Time<T1> {
    type Output = Time<T1>;

    fn add(self, rhs: Time<T2>) -> Self::Output {
        Time::new(self.value + rhs.get::<T1>())
    }
}

impl<A: LengthUnit, B: LengthUnit> ops::Div<Length<B>> for Length<A> {
    type Output = f32;

    fn div(self, rhs: Length<B>) -> Self::Output {
        (A::apply(self.value as _, 1) / B::apply(rhs.value as _, 1)) as _
    }
}

impl<L: LengthUnit, T: TimeUnit> ops::Div<Time<T>> for Length<L> {
    type Output = Velocity<L, T>;

    fn div(self, rhs: Time<T>) -> Self::Output {
        Velocity::new(self.value / rhs.value)
    }
}

impl<L1: LengthUnit, L2: LengthUnit, T2: TimeUnit> ops::Div<Velocity<L2, T2>> for Length<L1> {
    type Output = Time<T2>;

    fn div(self, rhs: Velocity<L2, T2>) -> Self::Output {
        Time::new((L1::apply(self.value as _, 1) / L2::apply(rhs.value as _, 1)) as _)
    }
}

impl<L1: LengthUnit, L2: LengthUnit> ops::Mul<Length<L2>> for Length<L1> {
    type Output = Area<L1>;

    fn mul(self, rhs: Length<L2>) -> Self::Output {
        let base = L1::apply(self.value as _, 1) * L2::apply(rhs.value as _, 1);
        Area::new(L1::apply(base, -2) as _)
    }
}

impl<L1: LengthUnit, L2: LengthUnit> ops::Mul<Length<L2>> for Area<L1> {
    type Output = Volume<L1>;

    fn mul(self, rhs: Length<L2>) -> Self::Output {
        let base = L1::apply(self.value as _, 2) * L2::apply(rhs.value as _, 1);
        Volume::new(L1::apply(base, -3) as _)
    }
}

macro_rules! quantity {
    ($($name:ident<$($param:ident: $constraint:ident $(= $default:ident)?),+>),*) => {
        $(
            pub struct $name<$($param: $constraint $(= $default)?),+> {
                value: f32,
                #[allow(unused_parens)]
                _unit: PhantomData<($($param),+)>,
            }

            impl<$($param: $constraint),+> $name<$($param),+> {
                pub fn new(value: f32) -> Self {
                    Self {
                        value,
                        _unit: PhantomData,
                    }
                }

                pub fn raw(&self) -> f32 {
                    self.value
                }

                pub fn raw_mut(&mut self) -> &mut f32 {
                    &mut self.value
                }
            }

            impl<$($param: $constraint),+> std::fmt::Debug for $name<$($param),+> {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    f.write_fmt(format_args!("{:?}", self.value))
                }
            }

            impl<$($param: $constraint),+> Default for $name<$($param),+> {
                fn default() -> Self {
                    Self::new(0.0)
                }
            }

            impl<$($param: $constraint),+> Clone for $name<$($param),+> {
                fn clone(&self) -> Self {
                    *self
                }
            }

            impl<$($param: $constraint),+> Copy for $name<$($param),+> {}

            impl<$($param: $constraint),+> PartialEq for $name<$($param),+> {
                fn eq(&self, other: &Self) -> bool {
                    self.value == other.value
                }
            }

            impl<$($param: $constraint),+> ops::Mul<f32> for $name<$($param),+> {
                type Output = Self;

                fn mul(mut self, rhs: f32) -> Self::Output {
                    self.value *= rhs;
                    self
                }
            }

            impl<$($param: $constraint),+> ops::Mul<$name<$($param),+>> for f32 {
                type Output = $name<$($param),+>;

                fn mul(self, rhs: $name<$($param),+>) -> Self::Output {
                    $name {
                        value: rhs.value * self,
                        _unit: PhantomData,
                    }
                }
            }

            impl<$($param: $constraint),+> ops::Div<f32> for $name<$($param),+> {
                type Output = Self;

                fn div(mut self, rhs: f32) -> Self::Output {
                    self.value /= rhs;
                    self
                }
            }

            impl<$($param: $constraint),+> ops::Div<$name<$($param),+>> for f32 {
                type Output = $name<$($param),+>;

                fn div(self, rhs: $name<$($param),+>) -> Self::Output {
                    $name {
                        value: self / rhs.value,
                        _unit: PhantomData,
                    }
                }
            }

            impl<$($param: $constraint),+> serde::Serialize for $name<$($param),+> {
                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where
                    S: serde::Serializer,
                {
                    serializer.serialize_f32(self.value)
                }
            }

            impl<'de, $($param: $constraint),+> serde::Deserialize<'de> for $name<$($param),+> {
                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where
                    D: serde::Deserializer<'de>,
                {
                    struct F32Visitor;

                    impl<'de> serde::de::Visitor<'de> for F32Visitor {
                        type Value = f32;

                        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                            formatter.write_str("a float")
                        }

                        fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E>
                        where
                            E: serde::de::Error,
                        {
                            Ok(value)
                        }

                        fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
                        where
                            E: serde::de::Error,
                        {
                            Ok(value as f32)
                        }
                    }

                    let value = deserializer.deserialize_f32(F32Visitor)?;
                    Ok($name {
                        value,
                        _unit: PhantomData,
                    })
                }
            }

        )*
    };
}

quantity! [
    Length<U: LengthUnit = Meter>,
    Area<U: LengthUnit = Meter>,
    Volume<U: LengthUnit = Meter>,
    Time<U: TimeUnit = Second>,
    Velocity<L: LengthUnit, T: TimeUnit>
];