1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Reading a measured value without knowing its type.
//!
//! Every Energistics measure has the same shape, in every version:
//!
//! ```xml
//! <ArealRotation uom="rad">0.0</ArealRotation>
//! ```
//!
//! and generates the same Rust shape - a text-bound number plus a `uom`
//! attribute:
//!
//! ```ignore
//! pub struct PlaneAngleMeasure {
//! #[energyml(text)] pub value: f64,
//! #[energyml(attr, xml_name = "uom")] pub uom: PlaneAngleUom,
//! }
//! ```
//!
//! What differs is the *type*: about 190 measure structs per version, each with
//! its own unit enum, none sharing a trait - roughly 730 unrelated types across
//! the four common versions. Anything wanting "the number and its unit" would
//! otherwise match on concrete types, once per version, forever.
//!
//! # Why the unit comes back as a string
//!
//! Because the Rust variant **is not stable across versions** and the wire
//! value is:
//!
//! | | eml 2.0 | eml 2.1 / 2.2 / 2.3 |
//! |---|---|---|
//! | `LengthUom::Mm` | megametre | **millimetre** |
//! | `LengthUom::Mm2` | millimetre | **megametre** |
//!
//! Six unit pairs flipped that way when the generator's case-disambiguation
//! changed (`MA`/`mA`, `MW`/`mW`, `MWb`/`mWb`, `Mcal[th]`/`mcal[th]`,
//! `dB.MW`/`dB.mW`). Code that matches `LengthUom::Mm` reads a length off by
//! **10⁹** on one version and is correct on the other, silently.
//!
//! The wire values - `m`, `ft`, `rad`, `dega` - did not change: 1500 unit
//! strings in eml 2.0, 1540 in eml 2.3, purely additive, nothing removed and
//! nothing respelled. So this module reads the *string* and never the variant,
//! and so must everything downstream.
//!
//! This module knows no unit names and converts nothing - that is schema
//! vocabulary and belongs above the core. It only knows the *shape*.
use crateBinding;
use crate;
/// A number and the unit it was written in.
/// Read any measure-shaped object.
///
/// `None` when the object is not a measure: no text-bound field, or one that
/// does not hold a number.
///
/// ```ignore
/// let rotation = measure::read(crs.field_object("ArealRotation")?)?;
/// assert_eq!(rotation.uom.as_deref(), Some("rad"));
/// ```
/// The text content of an element, as a number.
///
/// Found through the field whose binding says it is the text, never by
/// guessing at a name: the generator calls it `value`, but that is its
/// convention rather than a rule of the schema.
/// The `uom` attribute of an element, as written.
///
/// Every version spells the attribute `uom`; only the *enum* behind it changes,
/// and the enum is exactly what must not be matched on.
/// The value of an enumeration-typed element, as written.
///
/// The same argument as [`unit_of`]: an element like `<Uom>ft</Uom>` or
/// `<Direction>down</Direction>` carries schema vocabulary whose Rust variant
/// name is a generator artefact. Reading the lexical value keeps the caller
/// honest across versions.