Skip to main content

cu29_value_types/
lib.rs

1//! Allocation-free vocabulary for Copper value descriptions and logical metadata.
2//!
3//! Copper owns the catalogue. Numeric IDs are permanent: additions receive new IDs,
4//! and existing IDs must never be reused or renumbered.
5#![no_std]
6
7/// Scalar width and signedness. Integer encoding and endianness come from the codec configuration.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[repr(u32)]
11pub enum Scalar {
12    /// Native bool encoding.
13    Bool = 0,
14    /// Native u8 encoding.
15    U8 = 1,
16    /// Native u16 encoding.
17    U16 = 2,
18    /// Native u32 encoding.
19    U32 = 3,
20    /// Native u64 encoding.
21    U64 = 4,
22    /// Native u128 encoding.
23    U128 = 5,
24    /// Native i8 encoding.
25    I8 = 6,
26    /// Native i16 encoding.
27    I16 = 7,
28    /// Native i32 encoding.
29    I32 = 8,
30    /// Native i64 encoding.
31    I64 = 9,
32    /// Native i128 encoding.
33    I128 = 10,
34    /// Native f32 encoding.
35    F32 = 11,
36    /// Native f64 encoding.
37    F64 = 12,
38    /// Native char encoding.
39    Char = 13,
40}
41
42/// Exported shape of an aggregate.
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45#[repr(u32)]
46pub enum RecordShape {
47    /// Unit record.
48    Unit = 0,
49    /// Tuple record.
50    Tuple = 1,
51    /// One-field tuple struct or enum branch.
52    Newtype = 2,
53    /// Named record.
54    Struct = 3,
55}
56
57/// Shared source of truth for supported quantity wrappers and storage metadata.
58#[doc(hidden)]
59#[macro_export]
60macro_rules! __quantity_catalogue {
61    ($callback:ident $(, $argument:ident)*) => {
62        $callback! {
63            $($argument,)*
64            [
65                (1, absement, Absement, "m·s"),
66                (2, acceleration, Acceleration, "m·s⁻²"),
67                (3, action, Action, "J·s"),
68                (4, amount_of_substance, AmountOfSubstance, "mol"),
69                (5, angle, Angle, "rad"),
70                (6, angular_absement, AngularAbsement, "rad·s"),
71                (7, angular_acceleration, AngularAcceleration, "rad·s⁻²"),
72                (8, angular_jerk, AngularJerk, "rad·s⁻³"),
73                (9, angular_momentum, AngularMomentum, "N·m·s"),
74                (10, angular_velocity, AngularVelocity, "rad·s⁻¹"),
75                (11, area, Area, "m²"),
76                (12, areal_density_of_states, ArealDensityOfStates, "m⁻²·J⁻¹"),
77                (13, areal_heat_capacity, ArealHeatCapacity, "J·m⁻²·K⁻¹"),
78                (14, areal_mass_density, ArealMassDensity, "kg·m⁻²"),
79                (15, areal_number_density, ArealNumberDensity, "m⁻²"),
80                (16, areal_number_rate, ArealNumberRate, "m⁻²·s⁻¹"),
81                (17, available_energy, AvailableEnergy, "J·kg⁻¹"),
82                (18, capacitance, Capacitance, "F"),
83                (19, catalytic_activity, CatalyticActivity, "kat"),
84                (20, catalytic_activity_concentration, CatalyticActivityConcentration, "kat·m⁻³"),
85                (21, curvature, Curvature, "rad·m⁻¹"),
86                (22, diffusion_coefficient, DiffusionCoefficient, "m²·s⁻¹"),
87                (23, dynamic_viscosity, DynamicViscosity, "Pa·s"),
88                (24, electric_charge, ElectricCharge, "C"),
89                (25, electric_charge_areal_density, ElectricChargeArealDensity, "C·m⁻²"),
90                (26, electric_charge_linear_density, ElectricChargeLinearDensity, "C·m⁻¹"),
91                (27, electric_charge_volumetric_density, ElectricChargeVolumetricDensity, "C·m⁻³"),
92                (28, electric_current, ElectricCurrent, "A"),
93                (29, electric_current_density, ElectricCurrentDensity, "A·m⁻²"),
94                (30, electric_dipole_moment, ElectricDipoleMoment, "C·m"),
95                (31, electric_displacement_field, ElectricDisplacementField, "C·m⁻²"),
96                (32, electric_field, ElectricField, "V·m⁻¹"),
97                (33, electric_flux, ElectricFlux, "V·m"),
98                (34, electric_permittivity, ElectricPermittivity, "F·m⁻¹"),
99                (35, electric_potential, ElectricPotential, "V"),
100                (36, electric_quadrupole_moment, ElectricQuadrupoleMoment, "C·m²"),
101                (37, electrical_conductance, ElectricalConductance, "S"),
102                (38, electrical_conductivity, ElectricalConductivity, "S·m⁻¹"),
103                (39, electrical_mobility, ElectricalMobility, "m²·V⁻¹·s⁻¹"),
104                (40, electrical_resistance, ElectricalResistance, "Ω"),
105                (41, electrical_resistivity, ElectricalResistivity, "Ω·m"),
106                (42, energy, Energy, "J"),
107                (43, force, Force, "N"),
108                (44, frequency, Frequency, "Hz"),
109                (45, frequency_drift, FrequencyDrift, "Hz·s⁻¹"),
110                (46, heat_capacity, HeatCapacity, "J·K⁻¹"),
111                (47, heat_flux_density, HeatFluxDensity, "W·m⁻²"),
112                (48, heat_transfer, HeatTransfer, "W·m⁻²·K⁻¹"),
113                (49, inductance, Inductance, "H"),
114                (50, information, Information, "bit"),
115                (51, information_rate, InformationRate, "bit·s⁻¹"),
116                (52, inverse_velocity, InverseVelocity, "s·m⁻¹"),
117                (53, jerk, Jerk, "m·s⁻³"),
118                (54, kinematic_viscosity, KinematicViscosity, "m²·s⁻¹"),
119                (55, length, Length, "m"),
120                (56, linear_density_of_states, LinearDensityOfStates, "m⁻¹·J⁻¹"),
121                (57, linear_mass_density, LinearMassDensity, "kg·m⁻¹"),
122                (58, linear_number_density, LinearNumberDensity, "m⁻¹"),
123                (59, linear_number_rate, LinearNumberRate, "m⁻¹·s⁻¹"),
124                (60, linear_power_density, LinearPowerDensity, "W·m⁻¹"),
125                (61, luminance, Luminance, "cd·m⁻²"),
126                (62, luminous_intensity, LuminousIntensity, "cd"),
127                (63, magnetic_field_strength, MagneticFieldStrength, "A·m⁻¹"),
128                (64, magnetic_flux, MagneticFlux, "Wb"),
129                (65, magnetic_flux_density, MagneticFluxDensity, "T"),
130                (66, magnetic_moment, MagneticMoment, "A·m²"),
131                (67, magnetic_permeability, MagneticPermeability, "H·m⁻¹"),
132                (68, mass, Mass, "kg"),
133                (69, mass_concentration, MassConcentration, "kg·m⁻³"),
134                (70, mass_density, MassDensity, "kg·m⁻³"),
135                (71, mass_flux, MassFlux, "kg·m⁻²·s⁻¹"),
136                (72, mass_per_energy, MassPerEnergy, "kg·J⁻¹"),
137                (73, mass_rate, MassRate, "kg·s⁻¹"),
138                (74, molality, Molality, "mol·kg⁻¹"),
139                (75, molar_concentration, MolarConcentration, "mol·m⁻³"),
140                (76, molar_energy, MolarEnergy, "J·mol⁻¹"),
141                (77, molar_flux, MolarFlux, "mol·m⁻²·s⁻¹"),
142                (78, molar_heat_capacity, MolarHeatCapacity, "J·K⁻¹·mol⁻¹"),
143                (79, molar_mass, MolarMass, "kg·mol⁻¹"),
144                (80, molar_radioactivity, MolarRadioactivity, "Bq·mol⁻¹"),
145                (81, molar_volume, MolarVolume, "m³·mol⁻¹"),
146                (82, moment_of_inertia, MomentOfInertia, "kg·m²"),
147                (83, momentum, Momentum, "kg·m·s⁻¹"),
148                (84, power, Power, "W"),
149                (85, power_rate, PowerRate, "W·s⁻¹"),
150                (86, pressure, Pressure, "Pa"),
151                (87, radiant_exposure, RadiantExposure, "J·m⁻²"),
152                (88, radioactivity, Radioactivity, "Bq"),
153                (89, ratio, Ratio, "1"),
154                (90, reciprocal_length, ReciprocalLength, "m⁻¹"),
155                (91, solid_angle, SolidAngle, "sr"),
156                (92, specific_area, SpecificArea, "m²·kg⁻¹"),
157                (93, specific_heat_capacity, SpecificHeatCapacity, "J·kg⁻¹·K⁻¹"),
158                (94, specific_power, SpecificPower, "W·kg⁻¹"),
159                (95, specific_radioactivity, SpecificRadioactivity, "Bq·kg⁻¹"),
160                (96, specific_volume, SpecificVolume, "m³·kg⁻¹"),
161                (97, surface_electric_current_density, SurfaceElectricCurrentDensity, "A·m⁻¹"),
162                (98, surface_tension, SurfaceTension, "N·m⁻¹"),
163                (99, temperature_coefficient, TemperatureCoefficient, "K⁻¹"),
164                (100, temperature_gradient, TemperatureGradient, "K·m⁻¹"),
165                (101, temperature_interval, TemperatureInterval, "K"),
166                (102, thermal_conductance, ThermalConductance, "W·K⁻¹"),
167                (103, thermal_conductivity, ThermalConductivity, "W·m⁻¹·K⁻¹"),
168                (104, thermal_resistance, ThermalResistance, "K·W⁻¹"),
169                (105, thermodynamic_temperature, ThermodynamicTemperature, "K"),
170                (106, time, Time, "s"),
171                (107, torque, Torque, "N·m"),
172                (108, velocity, Velocity, "m·s⁻¹"),
173                (109, volume, Volume, "m³"),
174                (110, volume_rate, VolumeRate, "m³·s⁻¹"),
175                (111, volumetric_density_of_states, VolumetricDensityOfStates, "m⁻³·J⁻¹"),
176                (112, volumetric_heat_capacity, VolumetricHeatCapacity, "J·m⁻³·K⁻¹"),
177                (113, volumetric_number_density, VolumetricNumberDensity, "m⁻³"),
178                (114, volumetric_number_rate, VolumetricNumberRate, "m⁻³·s⁻¹"),
179                (115, volumetric_power_density, VolumetricPowerDensity, "W·m⁻³"),
180            ]
181        }
182    };
183}
184
185macro_rules! define_quantities {
186    ([$(($id:literal, $module:ident, $variant:ident, $unit:literal),)+]) => {
187        /// Copper's supported physical quantities, independent of scalar width.
188        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
189        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190        #[repr(u32)]
191        pub enum Quantity {
192            $(#[doc = concat!("Physical quantity `", stringify!($module), "`.")]
193            $variant = $id,)+
194        }
195
196        impl Quantity {
197            /// Every supported quantity, in catalogue order.
198            pub const ALL: &'static [Self] = &[$(Self::$variant,)+];
199
200            /// Permanent identity used in portable metadata.
201            pub const fn id(self) -> u32 { self as u32 }
202
203            /// Resolve a permanent identity supported by this Copper version.
204            pub const fn from_id(id: u32) -> Option<Self> {
205                match id { $($id => Some(Self::$variant),)+ _ => None }
206            }
207
208            /// Canonical quantity name for presentation.
209            pub const fn name(self) -> &'static str {
210                match self { $(Self::$variant => stringify!($module),)+ }
211            }
212
213            const fn coherent_unit_symbol(self) -> &'static str {
214                match self { $(Self::$variant => $unit,)+ }
215            }
216        }
217    };
218}
219__quantity_catalogue!(define_quantities);
220
221/// Physical storage unit, with coherent units identified by their quantity.
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
224pub enum StorageUnit {
225    /// The quantity's coherent storage unit, including named dimensionless units.
226    Coherent(Quantity),
227    /// One billionth of a second.
228    Nanosecond,
229}
230
231impl StorageUnit {
232    /// Permanent storage alternative ID, independent of presentation symbols.
233    pub const fn id(self) -> u32 {
234        match self {
235            Self::Coherent(_) => 1,
236            Self::Nanosecond => 2,
237        }
238    }
239
240    /// Presentation symbol, such as `m·s⁻¹`, `rad`, or `ns`.
241    pub const fn symbol(self) -> &'static str {
242        match self {
243            Self::Coherent(quantity) => quantity.coherent_unit_symbol(),
244            Self::Nanosecond => "ns",
245        }
246    }
247}
248
249/// Storage choices for Copper time quantities.
250#[derive(Clone, Copy, Debug, PartialEq, Eq)]
251#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
252pub enum TimeStorageUnit {
253    /// Coherent SI time storage.
254    Second,
255    /// Copper clock storage.
256    Nanosecond,
257}
258
259/// A physical quantity associated with a compatible storage unit.
260#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
262pub enum QuantityMetadata {
263    /// Coherent storage for a supported quantity.
264    Coherent(Quantity),
265    /// Copper's nanosecond time storage.
266    NanosecondTime,
267}
268
269impl QuantityMetadata {
270    /// Describe the quantity's coherent storage, including named dimensionless units.
271    pub const fn coherent(quantity: Quantity) -> Self {
272        Self::Coherent(quantity)
273    }
274
275    /// Describe time with an explicitly selected storage unit.
276    pub const fn time(unit: TimeStorageUnit) -> Self {
277        match unit {
278            TimeStorageUnit::Second => Self::Coherent(Quantity::Time),
279            TimeStorageUnit::Nanosecond => Self::NanosecondTime,
280        }
281    }
282
283    /// Physical quantity identity.
284    pub const fn quantity(self) -> Quantity {
285        match self {
286            Self::Coherent(quantity) => quantity,
287            Self::NanosecondTime => Quantity::Time,
288        }
289    }
290
291    /// Compatible storage unit.
292    pub const fn storage_unit(self) -> StorageUnit {
293        match self {
294            Self::Coherent(quantity) => StorageUnit::Coherent(quantity),
295            Self::NanosecondTime => StorageUnit::Nanosecond,
296        }
297    }
298}
299
300/// Copper-owned logical metadata attached to a value's schema.
301#[derive(Clone, Copy, Debug, PartialEq, Eq)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
303pub enum ValueMetadata {
304    /// Physical quantity and its actual encoded storage unit.
305    Quantity(QuantityMetadata),
306}
307
308impl ValueMetadata {
309    /// Permanent metadata kind ID used in length-delimited portable entries.
310    pub const fn kind_id(self) -> u32 {
311        match self {
312            Self::Quantity(_) => 1,
313        }
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320
321    #[test]
322    fn test_catalogue_ids_are_unique_and_resolve() {
323        for (index, quantity) in Quantity::ALL.iter().enumerate() {
324            assert_eq!(Quantity::from_id(quantity.id()), Some(*quantity));
325            assert!(
326                !Quantity::ALL[..index]
327                    .iter()
328                    .any(|previous| previous.id() == quantity.id())
329            );
330        }
331        assert_eq!(Quantity::from_id(0), None);
332        assert_eq!(Quantity::from_id(u32::MAX), None);
333    }
334
335    #[test]
336    fn test_quantity_storage_and_presentation() {
337        for (quantity, symbol) in [
338            (Quantity::Length, "m"),
339            (Quantity::Mass, "kg"),
340            (Quantity::Velocity, "m·s⁻¹"),
341            (Quantity::Angle, "rad"),
342            (Quantity::SolidAngle, "sr"),
343            (Quantity::Information, "bit"),
344            (Quantity::InformationRate, "bit·s⁻¹"),
345            (Quantity::Ratio, "1"),
346        ] {
347            let metadata = QuantityMetadata::coherent(quantity);
348            assert_eq!(metadata.quantity(), quantity);
349            assert_eq!(metadata.storage_unit(), StorageUnit::Coherent(quantity));
350            assert_eq!(metadata.storage_unit().symbol(), symbol);
351        }
352        assert_eq!(
353            QuantityMetadata::time(TimeStorageUnit::Second),
354            QuantityMetadata::coherent(Quantity::Time)
355        );
356        let nanos = QuantityMetadata::time(TimeStorageUnit::Nanosecond);
357        assert_eq!(nanos.quantity(), Quantity::Time);
358        assert_eq!(nanos.storage_unit(), StorageUnit::Nanosecond);
359        assert_eq!(nanos.storage_unit().symbol(), "ns");
360        assert_eq!(nanos.storage_unit().id(), 2);
361        assert_eq!(ValueMetadata::Quantity(nanos).kind_id(), 1);
362    }
363}