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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! [`ComponentRef`] — a qualified downward edge to one constituent passport.
use ;
use PassportRef;
/// How much of a constituent a bill of materials entry covers.
///
/// Product-group-neutral by construction. Core carries the number and the unit
/// and interprets neither: a `unit` is an opaque label here, not a member of a
/// vocabulary this crate validates against. Deciding that "cell" is counted and
/// "aluminium" is weighed is a product-group judgement, and no delegated act
/// defines it for any product group in force today.
///
/// `unit: None` means a dimensionless count — two of a thing, rather than two
/// kilograms of it.
///
/// `f64` matches the convention every other physical quantity in this crate
/// already uses (see `MaterialEntry::weight_kg` and `recycled_pct`). It is the
/// wrong type for exact arithmetic, and core performs none on it; a consumer
/// that needs exactness should not be reading it back out of a passport to do
/// arithmetic on anyway.
/// One constituent in a product's bill of materials.
///
/// Wraps [`PassportRef`] with the two qualifiers a downward edge needs. The
/// reference alone records *where* a constituent's passport is and *which hash*
/// pins it, but never how much of it or what part it plays — and a bill of
/// materials that cannot answer "how much of what, where" does not answer the
/// question a bill of materials exists for.
///
/// **Both qualifiers are optional, and core never interprets either.** That is
/// deliberate rather than unfinished: a battery module, a fibre lot and an
/// electronics sub-assembly are not the same kind of thing, and no delegated act
/// defines "component" or its granularity for any of our in-force product
/// groups. Core's job is to carry a pinned reference plus product-group-neutral
/// qualifiers and let product-group plugins interpret them. Hard-coding a
/// granularity here would commit every product group to one product group's
/// answer.
///
/// # This edge is a claim, not a consented fact
///
/// The hash-pin proves the target has not been modified. It does not prove the
/// target's operator agreed to being listed as a constituent, and BOM edges
/// deliberately carry **no** consent requirement: demanding a supplier signature
/// for every assembly is not something any real supply chain produces. A
/// `componentRef` is therefore a *claim by the assembler*, pinned so it cannot
/// be tampered with, and the verification walk reports it as exactly that.
///
/// This is the asymmetry with the upward direction, where
/// [`DerivationRef`](super::DerivationRef) moves regulatory responsibility under
/// Reg. (EU) 2023/1542 Art. 77(7) and so does need a consent artefact.
///
/// # Reads the shape this replaced, on purpose
///
/// `Deserialize` is hand-written rather than derived so that a bare
/// [`PassportRef`] — the element shape before this type existed — still parses,
/// arriving with no qualifiers. See the [`Deserialize`] impl for why that is not
/// optional.