Skip to main content

cu29_units/
lib.rs

1//! Copper-native SI quantity wrappers.
2//!
3//! Feature flags:
4//! - `default` = `["std"]`
5//! - `std`: enables `uom/std`
6//! - `reflect`: enables `bevy_reflect` support on wrapper types
7//! - `textlogs`: compatibility no-op for downstream feature forwarding
8//!
9#![cfg_attr(not(feature = "std"), no_std)]
10
11pub use uom;
12
13macro_rules! define_storage_wrappers {
14    ($storage_mod:ident, $storage_ty:ty, [$(($unit_mod:ident, $quantity:ident),)+]) => {
15        pub mod $storage_mod {
16            use core::marker::PhantomData;
17            use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19            #[cfg(feature = "reflect")]
20            use bevy_reflect::Reflect;
21
22            macro_rules! define_quantity {
23                ($unit_mod_name:ident, $quantity_name:ident) => {
24                    #[repr(transparent)]
25                    #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
26                    #[cfg_attr(feature = "reflect", derive(Reflect))]
27                    #[cfg_attr(feature = "reflect", reflect(from_reflect = false))]
28                    pub struct $quantity_name {
29                        pub value: $storage_ty,
30                    }
31
32                    impl $quantity_name {
33                        #[inline]
34                        pub fn new<U>(value: $storage_ty) -> Self
35                        where
36                            U: uom::si::$unit_mod_name::Conversion<$storage_ty>,
37                        {
38                            Self::from_uom(uom::si::$storage_mod::$quantity_name::new::<U>(value))
39                        }
40
41                        #[inline]
42                        pub fn get<U>(&self) -> $storage_ty
43                        where
44                            U: uom::si::$unit_mod_name::Conversion<$storage_ty>,
45                        {
46                            (*self).to_uom().get::<U>()
47                        }
48
49                        #[inline]
50                        pub fn raw(&self) -> $storage_ty {
51                            self.value
52                        }
53
54                        #[inline]
55                        pub fn into_uom(self) -> uom::si::$storage_mod::$quantity_name {
56                            self.to_uom()
57                        }
58
59                        #[inline]
60                        pub fn as_uom(&self) -> uom::si::$storage_mod::$quantity_name {
61                            (*self).to_uom()
62                        }
63
64                        #[inline]
65                        pub fn from_uom(inner: uom::si::$storage_mod::$quantity_name) -> Self {
66                            Self::from_base_value(inner.value)
67                        }
68
69                        #[inline]
70                        fn to_uom(self) -> uom::si::$storage_mod::$quantity_name {
71                            uom::si::$storage_mod::$quantity_name {
72                                dimension: PhantomData,
73                                units: PhantomData,
74                                value: self.value,
75                            }
76                        }
77
78                        #[inline]
79                        fn from_base_value(value: $storage_ty) -> Self {
80                            Self { value }
81                        }
82                    }
83
84                    impl Default for $quantity_name {
85                        fn default() -> Self {
86                            Self::from_base_value(0.0 as $storage_ty)
87                        }
88                    }
89
90                    impl From<uom::si::$storage_mod::$quantity_name> for $quantity_name {
91                        fn from(value: uom::si::$storage_mod::$quantity_name) -> Self {
92                            Self::from_uom(value)
93                        }
94                    }
95
96                    impl From<$quantity_name> for uom::si::$storage_mod::$quantity_name {
97                        fn from(value: $quantity_name) -> Self {
98                            value.into_uom()
99                        }
100                    }
101
102                    impl core::ops::Add for $quantity_name {
103                        type Output = Self;
104
105                        fn add(self, rhs: Self) -> Self::Output {
106                            Self::from_base_value(self.raw() + rhs.raw())
107                        }
108                    }
109
110                    impl core::ops::AddAssign for $quantity_name {
111                        fn add_assign(&mut self, rhs: Self) {
112                            *self = *self + rhs;
113                        }
114                    }
115
116                    impl core::ops::Sub for $quantity_name {
117                        type Output = Self;
118
119                        fn sub(self, rhs: Self) -> Self::Output {
120                            Self::from_base_value(self.raw() - rhs.raw())
121                        }
122                    }
123
124                    impl core::ops::SubAssign for $quantity_name {
125                        fn sub_assign(&mut self, rhs: Self) {
126                            *self = *self - rhs;
127                        }
128                    }
129
130                    impl core::ops::Mul<$storage_ty> for $quantity_name {
131                        type Output = Self;
132
133                        fn mul(self, rhs: $storage_ty) -> Self::Output {
134                            Self::from_base_value(self.raw() * rhs)
135                        }
136                    }
137
138                    impl core::ops::MulAssign<$storage_ty> for $quantity_name {
139                        fn mul_assign(&mut self, rhs: $storage_ty) {
140                            *self = *self * rhs;
141                        }
142                    }
143
144                    impl core::ops::Div<$storage_ty> for $quantity_name {
145                        type Output = Self;
146
147                        fn div(self, rhs: $storage_ty) -> Self::Output {
148                            Self::from_base_value(self.raw() / rhs)
149                        }
150                    }
151
152                    impl core::ops::DivAssign<$storage_ty> for $quantity_name {
153                        fn div_assign(&mut self, rhs: $storage_ty) {
154                            *self = *self / rhs;
155                        }
156                    }
157
158                    impl core::ops::Neg for $quantity_name {
159                        type Output = Self;
160
161                        fn neg(self) -> Self::Output {
162                            Self::from_base_value(-self.raw())
163                        }
164                    }
165
166                    impl Serialize for $quantity_name {
167                        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
168                        where
169                            S: Serializer,
170                        {
171                            self.raw().serialize(serializer)
172                        }
173                    }
174
175                    impl<'de> Deserialize<'de> for $quantity_name {
176                        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177                        where
178                            D: Deserializer<'de>,
179                        {
180                            let value = <$storage_ty>::deserialize(deserializer)?;
181                            Ok(Self::from_base_value(value))
182                        }
183                    }
184
185                    impl bincode::Encode for $quantity_name {
186                        fn encode<E: bincode::enc::Encoder>(
187                            &self,
188                            encoder: &mut E,
189                        ) -> Result<(), bincode::error::EncodeError> {
190                            bincode::Encode::encode(&self.raw(), encoder)
191                        }
192                    }
193
194                    impl<Context> bincode::Decode<Context> for $quantity_name {
195                        fn decode<D: bincode::de::Decoder<Context = Context>>(
196                            decoder: &mut D,
197                        ) -> Result<Self, bincode::error::DecodeError> {
198                            let value: $storage_ty = bincode::Decode::decode(decoder)?;
199                            Ok(Self::from_base_value(value))
200                        }
201                    }
202
203                    impl<'de, Context> bincode::BorrowDecode<'de, Context> for $quantity_name {
204                        fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
205                            decoder: &mut D,
206                        ) -> Result<Self, bincode::error::DecodeError> {
207                            <Self as bincode::Decode<Context>>::decode(decoder)
208                        }
209                    }
210
211                    #[cfg(feature = "reflect")]
212                    impl bevy_reflect::FromReflect for $quantity_name {
213                        fn from_reflect(
214                            reflect: &dyn bevy_reflect::PartialReflect,
215                        ) -> Option<Self> {
216                            if let Some(existing) = reflect.try_downcast_ref::<Self>() {
217                                return Some(*existing);
218                            }
219
220                            reflect
221                                .try_downcast_ref::<$storage_ty>()
222                                .map(|value| Self::from_base_value(*value))
223                        }
224                    }
225                };
226            }
227
228            $(define_quantity!($unit_mod, $quantity);)+
229        }
230    };
231}
232
233pub mod si {
234    pub use uom::si::{
235        ISQ, SI, absement, acceleration, action, amount_of_substance, angle, angular_absement,
236        angular_acceleration, angular_jerk, angular_momentum, angular_velocity, area,
237        areal_density_of_states, areal_heat_capacity, areal_mass_density, areal_number_density,
238        areal_number_rate, available_energy, capacitance, catalytic_activity,
239        catalytic_activity_concentration, curvature, diffusion_coefficient, dynamic_viscosity,
240        electric_charge, electric_charge_areal_density, electric_charge_linear_density,
241        electric_charge_volumetric_density, electric_current, electric_current_density,
242        electric_dipole_moment, electric_displacement_field, electric_field, electric_flux,
243        electric_permittivity, electric_potential, electric_quadrupole_moment,
244        electrical_conductance, electrical_conductivity, electrical_mobility,
245        electrical_resistance, electrical_resistivity, energy, force, frequency, frequency_drift,
246        heat_capacity, heat_flux_density, heat_transfer, inductance, information, information_rate,
247        inverse_velocity, jerk, kinematic_viscosity, length, linear_density_of_states,
248        linear_mass_density, linear_number_density, linear_number_rate, linear_power_density,
249        luminance, luminous_intensity, magnetic_field_strength, magnetic_flux,
250        magnetic_flux_density, magnetic_moment, magnetic_permeability, mass, mass_concentration,
251        mass_density, mass_flux, mass_per_energy, mass_rate, molality, molar_concentration,
252        molar_energy, molar_flux, molar_heat_capacity, molar_mass, molar_radioactivity,
253        molar_volume, moment_of_inertia, momentum, power, power_rate, pressure, radiant_exposure,
254        radioactivity, ratio, reciprocal_length, solid_angle, specific_area,
255        specific_heat_capacity, specific_power, specific_radioactivity, specific_volume,
256        surface_electric_current_density, surface_tension, temperature_coefficient,
257        temperature_gradient, temperature_interval, thermal_conductance, thermal_conductivity,
258        thermal_resistance, thermodynamic_temperature, time, torque, velocity, volume, volume_rate,
259        volumetric_density_of_states, volumetric_heat_capacity, volumetric_number_density,
260        volumetric_number_rate, volumetric_power_density,
261    };
262
263    define_storage_wrappers! {
264        f32,
265        f32,
266        [
267            (absement, Absement),
268            (acceleration, Acceleration),
269            (action, Action),
270            (amount_of_substance, AmountOfSubstance),
271            (angle, Angle),
272            (angular_absement, AngularAbsement),
273            (angular_acceleration, AngularAcceleration),
274            (angular_jerk, AngularJerk),
275            (angular_momentum, AngularMomentum),
276            (angular_velocity, AngularVelocity),
277            (area, Area),
278            (areal_density_of_states, ArealDensityOfStates),
279            (areal_heat_capacity, ArealHeatCapacity),
280            (areal_mass_density, ArealMassDensity),
281            (areal_number_density, ArealNumberDensity),
282            (areal_number_rate, ArealNumberRate),
283            (available_energy, AvailableEnergy),
284            (capacitance, Capacitance),
285            (catalytic_activity, CatalyticActivity),
286            (catalytic_activity_concentration, CatalyticActivityConcentration),
287            (curvature, Curvature),
288            (diffusion_coefficient, DiffusionCoefficient),
289            (dynamic_viscosity, DynamicViscosity),
290            (electric_charge, ElectricCharge),
291            (electric_charge_areal_density, ElectricChargeArealDensity),
292            (electric_charge_linear_density, ElectricChargeLinearDensity),
293            (electric_charge_volumetric_density, ElectricChargeVolumetricDensity),
294            (electric_current, ElectricCurrent),
295            (electric_current_density, ElectricCurrentDensity),
296            (electric_dipole_moment, ElectricDipoleMoment),
297            (electric_displacement_field, ElectricDisplacementField),
298            (electric_field, ElectricField),
299            (electric_flux, ElectricFlux),
300            (electric_permittivity, ElectricPermittivity),
301            (electric_potential, ElectricPotential),
302            (electric_quadrupole_moment, ElectricQuadrupoleMoment),
303            (electrical_conductance, ElectricalConductance),
304            (electrical_conductivity, ElectricalConductivity),
305            (electrical_mobility, ElectricalMobility),
306            (electrical_resistance, ElectricalResistance),
307            (electrical_resistivity, ElectricalResistivity),
308            (energy, Energy),
309            (force, Force),
310            (frequency, Frequency),
311            (frequency_drift, FrequencyDrift),
312            (heat_capacity, HeatCapacity),
313            (heat_flux_density, HeatFluxDensity),
314            (heat_transfer, HeatTransfer),
315            (inductance, Inductance),
316            (information, Information),
317            (information_rate, InformationRate),
318            (inverse_velocity, InverseVelocity),
319            (jerk, Jerk),
320            (kinematic_viscosity, KinematicViscosity),
321            (length, Length),
322            (linear_density_of_states, LinearDensityOfStates),
323            (linear_mass_density, LinearMassDensity),
324            (linear_number_density, LinearNumberDensity),
325            (linear_number_rate, LinearNumberRate),
326            (linear_power_density, LinearPowerDensity),
327            (luminance, Luminance),
328            (luminous_intensity, LuminousIntensity),
329            (magnetic_field_strength, MagneticFieldStrength),
330            (magnetic_flux, MagneticFlux),
331            (magnetic_flux_density, MagneticFluxDensity),
332            (magnetic_moment, MagneticMoment),
333            (magnetic_permeability, MagneticPermeability),
334            (mass, Mass),
335            (mass_concentration, MassConcentration),
336            (mass_density, MassDensity),
337            (mass_flux, MassFlux),
338            (mass_per_energy, MassPerEnergy),
339            (mass_rate, MassRate),
340            (molality, Molality),
341            (molar_concentration, MolarConcentration),
342            (molar_energy, MolarEnergy),
343            (molar_flux, MolarFlux),
344            (molar_heat_capacity, MolarHeatCapacity),
345            (molar_mass, MolarMass),
346            (molar_radioactivity, MolarRadioactivity),
347            (molar_volume, MolarVolume),
348            (moment_of_inertia, MomentOfInertia),
349            (momentum, Momentum),
350            (power, Power),
351            (power_rate, PowerRate),
352            (pressure, Pressure),
353            (radiant_exposure, RadiantExposure),
354            (radioactivity, Radioactivity),
355            (ratio, Ratio),
356            (reciprocal_length, ReciprocalLength),
357            (solid_angle, SolidAngle),
358            (specific_area, SpecificArea),
359            (specific_heat_capacity, SpecificHeatCapacity),
360            (specific_power, SpecificPower),
361            (specific_radioactivity, SpecificRadioactivity),
362            (specific_volume, SpecificVolume),
363            (surface_electric_current_density, SurfaceElectricCurrentDensity),
364            (surface_tension, SurfaceTension),
365            (temperature_coefficient, TemperatureCoefficient),
366            (temperature_gradient, TemperatureGradient),
367            (temperature_interval, TemperatureInterval),
368            (thermal_conductance, ThermalConductance),
369            (thermal_conductivity, ThermalConductivity),
370            (thermal_resistance, ThermalResistance),
371            (thermodynamic_temperature, ThermodynamicTemperature),
372            (time, Time),
373            (torque, Torque),
374            (velocity, Velocity),
375            (volume, Volume),
376            (volume_rate, VolumeRate),
377            (volumetric_density_of_states, VolumetricDensityOfStates),
378            (volumetric_heat_capacity, VolumetricHeatCapacity),
379            (volumetric_number_density, VolumetricNumberDensity),
380            (volumetric_number_rate, VolumetricNumberRate),
381            (volumetric_power_density, VolumetricPowerDensity),
382        ]
383    }
384
385    define_storage_wrappers! {
386        f64,
387        f64,
388        [
389            (absement, Absement),
390            (acceleration, Acceleration),
391            (action, Action),
392            (amount_of_substance, AmountOfSubstance),
393            (angle, Angle),
394            (angular_absement, AngularAbsement),
395            (angular_acceleration, AngularAcceleration),
396            (angular_jerk, AngularJerk),
397            (angular_momentum, AngularMomentum),
398            (angular_velocity, AngularVelocity),
399            (area, Area),
400            (areal_density_of_states, ArealDensityOfStates),
401            (areal_heat_capacity, ArealHeatCapacity),
402            (areal_mass_density, ArealMassDensity),
403            (areal_number_density, ArealNumberDensity),
404            (areal_number_rate, ArealNumberRate),
405            (available_energy, AvailableEnergy),
406            (capacitance, Capacitance),
407            (catalytic_activity, CatalyticActivity),
408            (catalytic_activity_concentration, CatalyticActivityConcentration),
409            (curvature, Curvature),
410            (diffusion_coefficient, DiffusionCoefficient),
411            (dynamic_viscosity, DynamicViscosity),
412            (electric_charge, ElectricCharge),
413            (electric_charge_areal_density, ElectricChargeArealDensity),
414            (electric_charge_linear_density, ElectricChargeLinearDensity),
415            (electric_charge_volumetric_density, ElectricChargeVolumetricDensity),
416            (electric_current, ElectricCurrent),
417            (electric_current_density, ElectricCurrentDensity),
418            (electric_dipole_moment, ElectricDipoleMoment),
419            (electric_displacement_field, ElectricDisplacementField),
420            (electric_field, ElectricField),
421            (electric_flux, ElectricFlux),
422            (electric_permittivity, ElectricPermittivity),
423            (electric_potential, ElectricPotential),
424            (electric_quadrupole_moment, ElectricQuadrupoleMoment),
425            (electrical_conductance, ElectricalConductance),
426            (electrical_conductivity, ElectricalConductivity),
427            (electrical_mobility, ElectricalMobility),
428            (electrical_resistance, ElectricalResistance),
429            (electrical_resistivity, ElectricalResistivity),
430            (energy, Energy),
431            (force, Force),
432            (frequency, Frequency),
433            (frequency_drift, FrequencyDrift),
434            (heat_capacity, HeatCapacity),
435            (heat_flux_density, HeatFluxDensity),
436            (heat_transfer, HeatTransfer),
437            (inductance, Inductance),
438            (information, Information),
439            (information_rate, InformationRate),
440            (inverse_velocity, InverseVelocity),
441            (jerk, Jerk),
442            (kinematic_viscosity, KinematicViscosity),
443            (length, Length),
444            (linear_density_of_states, LinearDensityOfStates),
445            (linear_mass_density, LinearMassDensity),
446            (linear_number_density, LinearNumberDensity),
447            (linear_number_rate, LinearNumberRate),
448            (linear_power_density, LinearPowerDensity),
449            (luminance, Luminance),
450            (luminous_intensity, LuminousIntensity),
451            (magnetic_field_strength, MagneticFieldStrength),
452            (magnetic_flux, MagneticFlux),
453            (magnetic_flux_density, MagneticFluxDensity),
454            (magnetic_moment, MagneticMoment),
455            (magnetic_permeability, MagneticPermeability),
456            (mass, Mass),
457            (mass_concentration, MassConcentration),
458            (mass_density, MassDensity),
459            (mass_flux, MassFlux),
460            (mass_per_energy, MassPerEnergy),
461            (mass_rate, MassRate),
462            (molality, Molality),
463            (molar_concentration, MolarConcentration),
464            (molar_energy, MolarEnergy),
465            (molar_flux, MolarFlux),
466            (molar_heat_capacity, MolarHeatCapacity),
467            (molar_mass, MolarMass),
468            (molar_radioactivity, MolarRadioactivity),
469            (molar_volume, MolarVolume),
470            (moment_of_inertia, MomentOfInertia),
471            (momentum, Momentum),
472            (power, Power),
473            (power_rate, PowerRate),
474            (pressure, Pressure),
475            (radiant_exposure, RadiantExposure),
476            (radioactivity, Radioactivity),
477            (ratio, Ratio),
478            (reciprocal_length, ReciprocalLength),
479            (solid_angle, SolidAngle),
480            (specific_area, SpecificArea),
481            (specific_heat_capacity, SpecificHeatCapacity),
482            (specific_power, SpecificPower),
483            (specific_radioactivity, SpecificRadioactivity),
484            (specific_volume, SpecificVolume),
485            (surface_electric_current_density, SurfaceElectricCurrentDensity),
486            (surface_tension, SurfaceTension),
487            (temperature_coefficient, TemperatureCoefficient),
488            (temperature_gradient, TemperatureGradient),
489            (temperature_interval, TemperatureInterval),
490            (thermal_conductance, ThermalConductance),
491            (thermal_conductivity, ThermalConductivity),
492            (thermal_resistance, ThermalResistance),
493            (thermodynamic_temperature, ThermodynamicTemperature),
494            (time, Time),
495            (torque, Torque),
496            (velocity, Velocity),
497            (volume, Volume),
498            (volume_rate, VolumeRate),
499            (volumetric_density_of_states, VolumetricDensityOfStates),
500            (volumetric_heat_capacity, VolumetricHeatCapacity),
501            (volumetric_number_density, VolumetricNumberDensity),
502            (volumetric_number_rate, VolumetricNumberRate),
503            (volumetric_power_density, VolumetricPowerDensity),
504        ]
505    }
506}
507
508#[cfg(all(test, feature = "reflect"))]
509mod tests {
510    use super::si::f32::Velocity;
511    use super::si::velocity::{kilometer_per_hour, meter_per_second};
512    use bevy_reflect::{PartialReflect, Reflect, ReflectRef};
513
514    #[derive(Reflect)]
515    #[reflect(from_reflect = false)]
516    struct Msg {
517        speed: Velocity,
518    }
519
520    #[test]
521    fn reflect_velocity_exposes_value_field() {
522        let msg = Msg {
523            speed: Velocity::new::<kilometer_per_hour>(36.0),
524        };
525
526        assert!(matches!(
527            msg.speed.as_partial_reflect().reflect_ref(),
528            ReflectRef::Struct(_)
529        ));
530        assert_eq!(msg.speed.get::<meter_per_second>(), 10.0);
531
532        let speed_reflected = match msg.as_partial_reflect().reflect_ref() {
533            ReflectRef::Struct(s) => s.field("speed").expect("speed field should exist"),
534            _ => panic!("expected struct reflection"),
535        };
536
537        let speed = speed_reflected
538            .try_downcast_ref::<Velocity>()
539            .expect("speed should downcast to cu29_units::si::f32::Velocity");
540        assert_eq!(speed.raw(), 10.0);
541        assert_eq!(speed.value, 10.0);
542        assert_eq!(speed.get::<meter_per_second>(), 10.0);
543    }
544}