Skip to main content

active_infinity/
unit.rs

1//! Unit discriminants for `Quantity` values.
2//!
3//! Units are a *closed* set of stable numeric discriminants plus a named
4//! escape hatch, so the wire format never depends on the spelling table of
5//! any particular `active-core` release. Ids in the range `0..=66` are the
6//! built-in physical units and are permanent: a reader that meets an id it
7//! does not implement must reject the buffer (`infinity/unknown-unit`)
8//! rather than guess. Custom units travel as `Named` and are matched by
9//! exact UTF-8 name.
10//!
11//! The numeric ids are part of the wire contract (`docs/spec/layout.md`
12//! §5.2). They mirror the order of `active_core::types::Unit`, which is
13//! intentional: the interop layer maps one-to-one, but the layout spec — not
14//! that enum — is authoritative.
15
16use std::fmt;
17
18/// Discriminant reserved for a named (custom) unit.
19pub const UNIT_NAMED: u32 = 0xFFFF;
20/// Highest built-in discriminant implemented by this format version.
21pub const UNIT_MAX_BUILTIN: u32 = 66;
22
23/// A unit attached to a quantity.
24// The discriminants and their numeric ids are specified in
25// `docs/spec/layout.md` §5.2; variant names are the unit identifiers.
26#[allow(missing_docs)]
27#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub enum UnitTag {
29    // Time (canonical: nanoseconds)
30    Nanoseconds,
31    Microseconds,
32    Milliseconds,
33    Seconds,
34    Minutes,
35    Hours,
36    Days,
37    Weeks,
38    // Information (binary)
39    Bytes,
40    Kibibytes,
41    Mebibytes,
42    Gibibytes,
43    Tebibytes,
44    Pebibytes,
45    Exbibytes,
46    // Information (decimal)
47    Kilobytes,
48    Megabytes,
49    Gigabytes,
50    Terabytes,
51    // Rates
52    PerSecond,
53    PerMinute,
54    PerHour,
55    RequestsPerSecond,
56    RequestsPerMinute,
57    RequestsPerHour,
58    // Ratios and frequencies
59    Percent,
60    Hertz,
61    Kilohertz,
62    Megahertz,
63    Gigahertz,
64    Terahertz,
65    // Bits and bit rates
66    Bits,
67    Kilobits,
68    Megabits,
69    Gigabits,
70    BitsPerSecond,
71    KilobitsPerSecond,
72    MegabitsPerSecond,
73    GigabitsPerSecond,
74    MegabytesPerSecond,
75    // AI-age units
76    Tokens,
77    KiloTokens,
78    MegaTokens,
79    TokensPerSecond,
80    Flops,
81    TeraFlops,
82    PetaFlops,
83    // Domain SI
84    Pascal,
85    Bar,
86    Psi,
87    Volts,
88    Amperes,
89    Watts,
90    Joules,
91    KilowattHours,
92    Newtons,
93    Degrees,
94    Radians,
95    Rpm,
96    Meters,
97    Kilometers,
98    Kilograms,
99    Grams,
100    Kelvin,
101    Celsius,
102    Fahrenheit,
103    Decibel,
104    /// A unit outside the built-in table, identified by its exact name.
105    Named(String),
106}
107
108impl UnitTag {
109    /// Stable wire discriminant. `Named` is not a discriminant on its own;
110    /// callers must also carry the name in the unit table entry.
111    pub fn discriminant(&self) -> u32 {
112        match self {
113            UnitTag::Nanoseconds => 0,
114            UnitTag::Microseconds => 1,
115            UnitTag::Milliseconds => 2,
116            UnitTag::Seconds => 3,
117            UnitTag::Minutes => 4,
118            UnitTag::Hours => 5,
119            UnitTag::Days => 6,
120            UnitTag::Weeks => 7,
121            UnitTag::Bytes => 8,
122            UnitTag::Kibibytes => 9,
123            UnitTag::Mebibytes => 10,
124            UnitTag::Gibibytes => 11,
125            UnitTag::Tebibytes => 12,
126            UnitTag::Pebibytes => 13,
127            UnitTag::Exbibytes => 14,
128            UnitTag::Kilobytes => 15,
129            UnitTag::Megabytes => 16,
130            UnitTag::Gigabytes => 17,
131            UnitTag::Terabytes => 18,
132            UnitTag::PerSecond => 19,
133            UnitTag::PerMinute => 20,
134            UnitTag::PerHour => 21,
135            UnitTag::RequestsPerSecond => 22,
136            UnitTag::RequestsPerMinute => 23,
137            UnitTag::RequestsPerHour => 24,
138            UnitTag::Percent => 25,
139            UnitTag::Hertz => 26,
140            UnitTag::Kilohertz => 27,
141            UnitTag::Megahertz => 28,
142            UnitTag::Gigahertz => 29,
143            UnitTag::Terahertz => 30,
144            UnitTag::Bits => 31,
145            UnitTag::Kilobits => 32,
146            UnitTag::Megabits => 33,
147            UnitTag::Gigabits => 34,
148            UnitTag::BitsPerSecond => 35,
149            UnitTag::KilobitsPerSecond => 36,
150            UnitTag::MegabitsPerSecond => 37,
151            UnitTag::GigabitsPerSecond => 38,
152            UnitTag::MegabytesPerSecond => 39,
153            UnitTag::Tokens => 40,
154            UnitTag::KiloTokens => 41,
155            UnitTag::MegaTokens => 42,
156            UnitTag::TokensPerSecond => 43,
157            UnitTag::Flops => 44,
158            UnitTag::TeraFlops => 45,
159            UnitTag::PetaFlops => 46,
160            UnitTag::Pascal => 47,
161            UnitTag::Bar => 48,
162            UnitTag::Psi => 49,
163            UnitTag::Volts => 50,
164            UnitTag::Amperes => 51,
165            UnitTag::Watts => 52,
166            UnitTag::Joules => 53,
167            UnitTag::KilowattHours => 54,
168            UnitTag::Newtons => 55,
169            UnitTag::Degrees => 56,
170            UnitTag::Radians => 57,
171            UnitTag::Rpm => 58,
172            UnitTag::Meters => 59,
173            UnitTag::Kilometers => 60,
174            UnitTag::Kilograms => 61,
175            UnitTag::Grams => 62,
176            UnitTag::Kelvin => 63,
177            UnitTag::Celsius => 64,
178            UnitTag::Fahrenheit => 65,
179            UnitTag::Decibel => 66,
180            UnitTag::Named(_) => UNIT_NAMED,
181        }
182    }
183
184    /// Built-in unit for a discriminant, or `None` when out of range.
185    pub fn from_discriminant(disc: u32) -> Option<UnitTag> {
186        let unit = match disc {
187            0 => UnitTag::Nanoseconds,
188            1 => UnitTag::Microseconds,
189            2 => UnitTag::Milliseconds,
190            3 => UnitTag::Seconds,
191            4 => UnitTag::Minutes,
192            5 => UnitTag::Hours,
193            6 => UnitTag::Days,
194            7 => UnitTag::Weeks,
195            8 => UnitTag::Bytes,
196            9 => UnitTag::Kibibytes,
197            10 => UnitTag::Mebibytes,
198            11 => UnitTag::Gibibytes,
199            12 => UnitTag::Tebibytes,
200            13 => UnitTag::Pebibytes,
201            14 => UnitTag::Exbibytes,
202            15 => UnitTag::Kilobytes,
203            16 => UnitTag::Megabytes,
204            17 => UnitTag::Gigabytes,
205            18 => UnitTag::Terabytes,
206            19 => UnitTag::PerSecond,
207            20 => UnitTag::PerMinute,
208            21 => UnitTag::PerHour,
209            22 => UnitTag::RequestsPerSecond,
210            23 => UnitTag::RequestsPerMinute,
211            24 => UnitTag::RequestsPerHour,
212            25 => UnitTag::Percent,
213            26 => UnitTag::Hertz,
214            27 => UnitTag::Kilohertz,
215            28 => UnitTag::Megahertz,
216            29 => UnitTag::Gigahertz,
217            30 => UnitTag::Terahertz,
218            31 => UnitTag::Bits,
219            32 => UnitTag::Kilobits,
220            33 => UnitTag::Megabits,
221            34 => UnitTag::Gigabits,
222            35 => UnitTag::BitsPerSecond,
223            36 => UnitTag::KilobitsPerSecond,
224            37 => UnitTag::MegabitsPerSecond,
225            38 => UnitTag::GigabitsPerSecond,
226            39 => UnitTag::MegabytesPerSecond,
227            40 => UnitTag::Tokens,
228            41 => UnitTag::KiloTokens,
229            42 => UnitTag::MegaTokens,
230            43 => UnitTag::TokensPerSecond,
231            44 => UnitTag::Flops,
232            45 => UnitTag::TeraFlops,
233            46 => UnitTag::PetaFlops,
234            47 => UnitTag::Pascal,
235            48 => UnitTag::Bar,
236            49 => UnitTag::Psi,
237            50 => UnitTag::Volts,
238            51 => UnitTag::Amperes,
239            52 => UnitTag::Watts,
240            53 => UnitTag::Joules,
241            54 => UnitTag::KilowattHours,
242            55 => UnitTag::Newtons,
243            56 => UnitTag::Degrees,
244            57 => UnitTag::Radians,
245            58 => UnitTag::Rpm,
246            59 => UnitTag::Meters,
247            60 => UnitTag::Kilometers,
248            61 => UnitTag::Kilograms,
249            62 => UnitTag::Grams,
250            63 => UnitTag::Kelvin,
251            64 => UnitTag::Celsius,
252            65 => UnitTag::Fahrenheit,
253            66 => UnitTag::Decibel,
254            _ => return None,
255        };
256        Some(unit)
257    }
258
259    /// Stable textual name, independent of source spelling.
260    pub fn name(&self) -> &str {
261        match self {
262            UnitTag::Nanoseconds => "nanoseconds",
263            UnitTag::Microseconds => "microseconds",
264            UnitTag::Milliseconds => "milliseconds",
265            UnitTag::Seconds => "seconds",
266            UnitTag::Minutes => "minutes",
267            UnitTag::Hours => "hours",
268            UnitTag::Days => "days",
269            UnitTag::Weeks => "weeks",
270            UnitTag::Bytes => "bytes",
271            UnitTag::Kibibytes => "kibibytes",
272            UnitTag::Mebibytes => "mebibytes",
273            UnitTag::Gibibytes => "gibibytes",
274            UnitTag::Tebibytes => "tebibytes",
275            UnitTag::Pebibytes => "pebibytes",
276            UnitTag::Exbibytes => "exbibytes",
277            UnitTag::Kilobytes => "kilobytes",
278            UnitTag::Megabytes => "megabytes",
279            UnitTag::Gigabytes => "gigabytes",
280            UnitTag::Terabytes => "terabytes",
281            UnitTag::PerSecond => "per-second",
282            UnitTag::PerMinute => "per-minute",
283            UnitTag::PerHour => "per-hour",
284            UnitTag::RequestsPerSecond => "requests-per-second",
285            UnitTag::RequestsPerMinute => "requests-per-minute",
286            UnitTag::RequestsPerHour => "requests-per-hour",
287            UnitTag::Percent => "percent",
288            UnitTag::Hertz => "hertz",
289            UnitTag::Kilohertz => "kilohertz",
290            UnitTag::Megahertz => "megahertz",
291            UnitTag::Gigahertz => "gigahertz",
292            UnitTag::Terahertz => "terahertz",
293            UnitTag::Bits => "bits",
294            UnitTag::Kilobits => "kilobits",
295            UnitTag::Megabits => "megabits",
296            UnitTag::Gigabits => "gigabits",
297            UnitTag::BitsPerSecond => "bits-per-second",
298            UnitTag::KilobitsPerSecond => "kilobits-per-second",
299            UnitTag::MegabitsPerSecond => "megabits-per-second",
300            UnitTag::GigabitsPerSecond => "gigabits-per-second",
301            UnitTag::MegabytesPerSecond => "megabytes-per-second",
302            UnitTag::Tokens => "tokens",
303            UnitTag::KiloTokens => "kilo-tokens",
304            UnitTag::MegaTokens => "mega-tokens",
305            UnitTag::TokensPerSecond => "tokens-per-second",
306            UnitTag::Flops => "flops",
307            UnitTag::TeraFlops => "teraflops",
308            UnitTag::PetaFlops => "petaflops",
309            UnitTag::Pascal => "pascal",
310            UnitTag::Bar => "bar",
311            UnitTag::Psi => "psi",
312            UnitTag::Volts => "volts",
313            UnitTag::Amperes => "amperes",
314            UnitTag::Watts => "watts",
315            UnitTag::Joules => "joules",
316            UnitTag::KilowattHours => "kilowatt-hours",
317            UnitTag::Newtons => "newtons",
318            UnitTag::Degrees => "degrees",
319            UnitTag::Radians => "radians",
320            UnitTag::Rpm => "rpm",
321            UnitTag::Meters => "meters",
322            UnitTag::Kilometers => "kilometers",
323            UnitTag::Kilograms => "kilograms",
324            UnitTag::Grams => "grams",
325            UnitTag::Kelvin => "kelvin",
326            UnitTag::Celsius => "celsius",
327            UnitTag::Fahrenheit => "fahrenheit",
328            UnitTag::Decibel => "decibel",
329            UnitTag::Named(name) => name,
330        }
331    }
332
333    /// Absolute temperatures and decibels admit equality and ordering but
334    /// never arithmetic (`active-core` spec Ch. 4 §4.1). Preserved so the
335    /// binary form cannot be used to smuggle an illegal operation past the
336    /// evaluator.
337    pub fn is_non_arithmetic(&self) -> bool {
338        matches!(
339            self,
340            UnitTag::Kelvin | UnitTag::Celsius | UnitTag::Fahrenheit | UnitTag::Decibel
341        )
342    }
343
344    /// True for the closed built-in table and false for `Named`.
345    pub fn is_builtin(&self) -> bool {
346        !matches!(self, UnitTag::Named(_))
347    }
348}
349
350impl fmt::Display for UnitTag {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        match self {
353            // A named unit is free text and can hold a secret copy. The raw
354            // name stays available through `name()` for a deliberate transfer.
355            UnitTag::Named(name) => write!(f, "named-unit(<{} bytes>)", name.len()),
356            other => f.write_str(other.name()),
357        }
358    }
359}
360
361impl fmt::Debug for UnitTag {
362    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363        // A named unit is caller text and can carry a secret copy. Debug prints
364        // its size; built-in discriminants are a closed, safe set.
365        match self {
366            UnitTag::Named(name) => write!(f, "Named(<{} bytes>)", name.len()),
367            other => f.write_str(other.name()),
368        }
369    }
370}
371
372#[cfg(feature = "active-core")]
373mod interop {
374    use super::UnitTag;
375    use active_core::Unit;
376
377    impl From<&Unit> for UnitTag {
378        fn from(unit: &Unit) -> Self {
379            match unit {
380                Unit::Nanoseconds => UnitTag::Nanoseconds,
381                Unit::Microseconds => UnitTag::Microseconds,
382                Unit::Milliseconds => UnitTag::Milliseconds,
383                Unit::Seconds => UnitTag::Seconds,
384                Unit::Minutes => UnitTag::Minutes,
385                Unit::Hours => UnitTag::Hours,
386                Unit::Days => UnitTag::Days,
387                Unit::Weeks => UnitTag::Weeks,
388                Unit::Bytes => UnitTag::Bytes,
389                Unit::Kibibytes => UnitTag::Kibibytes,
390                Unit::Mebibytes => UnitTag::Mebibytes,
391                Unit::Gibibytes => UnitTag::Gibibytes,
392                Unit::Tebibytes => UnitTag::Tebibytes,
393                Unit::Pebibytes => UnitTag::Pebibytes,
394                Unit::Exbibytes => UnitTag::Exbibytes,
395                Unit::Kilobytes => UnitTag::Kilobytes,
396                Unit::Megabytes => UnitTag::Megabytes,
397                Unit::Gigabytes => UnitTag::Gigabytes,
398                Unit::Terabytes => UnitTag::Terabytes,
399                Unit::PerSecond => UnitTag::PerSecond,
400                Unit::PerMinute => UnitTag::PerMinute,
401                Unit::PerHour => UnitTag::PerHour,
402                Unit::RequestsPerSecond => UnitTag::RequestsPerSecond,
403                Unit::RequestsPerMinute => UnitTag::RequestsPerMinute,
404                Unit::RequestsPerHour => UnitTag::RequestsPerHour,
405                Unit::Percent => UnitTag::Percent,
406                Unit::Hertz => UnitTag::Hertz,
407                Unit::Kilohertz => UnitTag::Kilohertz,
408                Unit::Megahertz => UnitTag::Megahertz,
409                Unit::Gigahertz => UnitTag::Gigahertz,
410                Unit::Terahertz => UnitTag::Terahertz,
411                Unit::Bits => UnitTag::Bits,
412                Unit::Kilobits => UnitTag::Kilobits,
413                Unit::Megabits => UnitTag::Megabits,
414                Unit::Gigabits => UnitTag::Gigabits,
415                Unit::BitsPerSecond => UnitTag::BitsPerSecond,
416                Unit::KilobitsPerSecond => UnitTag::KilobitsPerSecond,
417                Unit::MegabitsPerSecond => UnitTag::MegabitsPerSecond,
418                Unit::GigabitsPerSecond => UnitTag::GigabitsPerSecond,
419                Unit::MegabytesPerSecond => UnitTag::MegabytesPerSecond,
420                Unit::Tokens => UnitTag::Tokens,
421                Unit::KiloTokens => UnitTag::KiloTokens,
422                Unit::MegaTokens => UnitTag::MegaTokens,
423                Unit::TokensPerSecond => UnitTag::TokensPerSecond,
424                Unit::Flops => UnitTag::Flops,
425                Unit::TeraFlops => UnitTag::TeraFlops,
426                Unit::PetaFlops => UnitTag::PetaFlops,
427                Unit::Pascal => UnitTag::Pascal,
428                Unit::Bar => UnitTag::Bar,
429                Unit::Psi => UnitTag::Psi,
430                Unit::Volts => UnitTag::Volts,
431                Unit::Amperes => UnitTag::Amperes,
432                Unit::Watts => UnitTag::Watts,
433                Unit::Joules => UnitTag::Joules,
434                Unit::KilowattHours => UnitTag::KilowattHours,
435                Unit::Newtons => UnitTag::Newtons,
436                Unit::Degrees => UnitTag::Degrees,
437                Unit::Radians => UnitTag::Radians,
438                Unit::Rpm => UnitTag::Rpm,
439                Unit::Meters => UnitTag::Meters,
440                Unit::Kilometers => UnitTag::Kilometers,
441                Unit::Kilograms => UnitTag::Kilograms,
442                Unit::Grams => UnitTag::Grams,
443                Unit::Kelvin => UnitTag::Kelvin,
444                Unit::Celsius => UnitTag::Celsius,
445                Unit::Fahrenheit => UnitTag::Fahrenheit,
446                Unit::Decibel => UnitTag::Decibel,
447                Unit::Custom(name) => UnitTag::Named(name.clone()),
448            }
449        }
450    }
451
452    impl From<&UnitTag> for Unit {
453        fn from(tag: &UnitTag) -> Self {
454            match tag {
455                UnitTag::Nanoseconds => Unit::Nanoseconds,
456                UnitTag::Microseconds => Unit::Microseconds,
457                UnitTag::Milliseconds => Unit::Milliseconds,
458                UnitTag::Seconds => Unit::Seconds,
459                UnitTag::Minutes => Unit::Minutes,
460                UnitTag::Hours => Unit::Hours,
461                UnitTag::Days => Unit::Days,
462                UnitTag::Weeks => Unit::Weeks,
463                UnitTag::Bytes => Unit::Bytes,
464                UnitTag::Kibibytes => Unit::Kibibytes,
465                UnitTag::Mebibytes => Unit::Mebibytes,
466                UnitTag::Gibibytes => Unit::Gibibytes,
467                UnitTag::Tebibytes => Unit::Tebibytes,
468                UnitTag::Pebibytes => Unit::Pebibytes,
469                UnitTag::Exbibytes => Unit::Exbibytes,
470                UnitTag::Kilobytes => Unit::Kilobytes,
471                UnitTag::Megabytes => Unit::Megabytes,
472                UnitTag::Gigabytes => Unit::Gigabytes,
473                UnitTag::Terabytes => Unit::Terabytes,
474                UnitTag::PerSecond => Unit::PerSecond,
475                UnitTag::PerMinute => Unit::PerMinute,
476                UnitTag::PerHour => Unit::PerHour,
477                UnitTag::RequestsPerSecond => Unit::RequestsPerSecond,
478                UnitTag::RequestsPerMinute => Unit::RequestsPerMinute,
479                UnitTag::RequestsPerHour => Unit::RequestsPerHour,
480                UnitTag::Percent => Unit::Percent,
481                UnitTag::Hertz => Unit::Hertz,
482                UnitTag::Kilohertz => Unit::Kilohertz,
483                UnitTag::Megahertz => Unit::Megahertz,
484                UnitTag::Gigahertz => Unit::Gigahertz,
485                UnitTag::Terahertz => Unit::Terahertz,
486                UnitTag::Bits => Unit::Bits,
487                UnitTag::Kilobits => Unit::Kilobits,
488                UnitTag::Megabits => Unit::Megabits,
489                UnitTag::Gigabits => Unit::Gigabits,
490                UnitTag::BitsPerSecond => Unit::BitsPerSecond,
491                UnitTag::KilobitsPerSecond => Unit::KilobitsPerSecond,
492                UnitTag::MegabitsPerSecond => Unit::MegabitsPerSecond,
493                UnitTag::GigabitsPerSecond => Unit::GigabitsPerSecond,
494                UnitTag::MegabytesPerSecond => Unit::MegabytesPerSecond,
495                UnitTag::Tokens => Unit::Tokens,
496                UnitTag::KiloTokens => Unit::KiloTokens,
497                UnitTag::MegaTokens => Unit::MegaTokens,
498                UnitTag::TokensPerSecond => Unit::TokensPerSecond,
499                UnitTag::Flops => Unit::Flops,
500                UnitTag::TeraFlops => Unit::TeraFlops,
501                UnitTag::PetaFlops => Unit::PetaFlops,
502                UnitTag::Pascal => Unit::Pascal,
503                UnitTag::Bar => Unit::Bar,
504                UnitTag::Psi => Unit::Psi,
505                UnitTag::Volts => Unit::Volts,
506                UnitTag::Amperes => Unit::Amperes,
507                UnitTag::Watts => Unit::Watts,
508                UnitTag::Joules => Unit::Joules,
509                UnitTag::KilowattHours => Unit::KilowattHours,
510                UnitTag::Newtons => Unit::Newtons,
511                UnitTag::Degrees => Unit::Degrees,
512                UnitTag::Radians => Unit::Radians,
513                UnitTag::Rpm => Unit::Rpm,
514                UnitTag::Meters => Unit::Meters,
515                UnitTag::Kilometers => Unit::Kilometers,
516                UnitTag::Kilograms => Unit::Kilograms,
517                UnitTag::Grams => Unit::Grams,
518                UnitTag::Kelvin => Unit::Kelvin,
519                UnitTag::Celsius => Unit::Celsius,
520                UnitTag::Fahrenheit => Unit::Fahrenheit,
521                UnitTag::Decibel => Unit::Decibel,
522                UnitTag::Named(name) => Unit::Custom(name.clone()),
523            }
524        }
525    }
526}
527
528#[cfg(test)]
529mod tests {
530    use super::*;
531
532    #[test]
533    fn discriminants_are_bijective() {
534        for disc in 0..=UNIT_MAX_BUILTIN {
535            let unit = UnitTag::from_discriminant(disc).expect("builtin in range");
536            assert_eq!(unit.discriminant(), disc, "round trip for {disc}");
537            assert!(unit.is_builtin());
538        }
539        assert!(UnitTag::from_discriminant(UNIT_MAX_BUILTIN + 1).is_none());
540        assert!(UnitTag::from_discriminant(UNIT_NAMED).is_none());
541    }
542
543    #[test]
544    fn named_units_keep_their_name() {
545        let unit = UnitTag::Named("widgets".to_string());
546        assert_eq!(unit.discriminant(), UNIT_NAMED);
547        assert_eq!(unit.name(), "widgets");
548        assert!(!unit.is_builtin());
549    }
550
551    #[test]
552    fn non_arithmetic_units_match_active_core() {
553        for unit in [
554            UnitTag::Kelvin,
555            UnitTag::Celsius,
556            UnitTag::Fahrenheit,
557            UnitTag::Decibel,
558        ] {
559            assert!(unit.is_non_arithmetic(), "{unit} must be non-arithmetic");
560        }
561        assert!(!UnitTag::Meters.is_non_arithmetic());
562    }
563}