1#![forbid(unsafe_code)]
15
16use dimvexa_core::{AmbiguousDef, Error, Exactness, Kind, PrefixMode, Registry, Shape, UnitDef};
17
18#[allow(
20 clippy::unreadable_literal,
21 clippy::too_many_lines,
22 clippy::unicode_not_nfc,
23 missing_docs
24)]
25mod generated {
26 use super::{AmbiguousDef, Exactness, Kind, PrefixMode, Shape, UnitDef};
27 include!(concat!(env!("OUT_DIR"), "/units_generated.rs"));
28}
29
30pub use generated::{AMBIGUOUS, DATA_HASH, DATA_VERSION, PACKS, UNITS};
31
32pub fn registry() -> Result<Registry, Error> {
39 Registry::from_defs(UNITS, AMBIGUOUS)
40}
41
42#[must_use]
44pub fn data_version() -> String {
45 format!("v{DATA_VERSION}+{DATA_HASH}")
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn table_loads_and_is_reasonably_sized() {
54 let reg = registry().unwrap();
55 assert!(reg.len() >= 50, "{}", reg.len());
56 assert_eq!(reg.len(), UNITS.len());
57 assert!(DATA_HASH.starts_with("fnv1a64:"));
58 assert!(PACKS.contains(&"base") && PACKS.contains(&"common"));
59 }
60
61 #[test]
62 fn every_ambiguous_symbol_is_rejected_with_candidates() {
63 let reg = registry().unwrap();
64 for a in AMBIGUOUS {
65 let err = reg.parse(a.symbol).unwrap_err();
66 assert_eq!(err.code(), "ambiguous_symbol", "{}", a.symbol);
67 assert_eq!(err.candidates().len(), a.candidates.len() + a.planned.len());
68 for c in a.candidates {
69 assert!(
70 reg.parse(c).is_ok(),
71 "candidate {c} of {} must resolve",
72 a.symbol
73 );
74 }
75 }
76 for sym in ["ton", "gal", "oz", "pt", "cal", "BTU", "mile", "ppm", "hp"] {
77 assert!(AMBIGUOUS.iter().any(|a| a.symbol == sym), "{sym}");
78 }
79 }
80}