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
//! Which products the repairability index reaches — Reg. (EU) 2023/1669 **Art. 1**.
//!
//! ✅ COMPLIANCE-PIN: EU 2023/1669, Art. 1 (OJ L 214, 31.8.2023, p. 12). Read
//! from the Official Journal text.
//!
//! ## Two regulations, two scopes, and the wider one is the one `DeviceType` follows
//!
//! Art. 1 of **2023/1669** — the energy-labelling regulation that enacts the
//! repairability index — establishes requirements for *"smartphones and slate
//! tablets"*. Art. 1(1) of **2023/1670**, its ecodesign twin, reaches
//! *"smartphones, other mobile phones, cordless phones and slate tablets"*.
//!
//! `DeviceType`'s four values come from the wider one, which is correct for what
//! that type is for and wrong as an input to this index. A cordless phone is not
//! a "mobile phone" under 2023/1670 Art. 2(1) at all, and neither it nor a
//! non-smart mobile phone is in 2023/1669's scope.
//!
//! ## The two exclusions, which are the reason this is not a two-line match
//!
//! Art. 1 continues, and both regulations carry the carve-out in identical words:
//!
//! > This Regulation does not apply to the following products:
//! > (a) mobile phones and tablets with a flexible main display which the user
//! > can unroll and roll up partly or fully;
//! > (b) smartphones for high security communication.
//!
//! A rollable-display phone and a high-security smartphone are both smartphones.
//! Nothing about the device type distinguishes them, so the exclusion has to be
//! **declared** — which is why this function takes it as a second argument rather
//! than deriving it.
//!
//! ## Why an undeclared exclusion means *not excluded*
//!
//! The carve-out is what removes the obligation, so silence cannot grant it: an
//! operator who says nothing has not claimed an exclusion, and reading the
//! absence as one would exempt a product on the strength of a missing field.
//!
//! That is the same fail-closed direction as
//! [`PassportScope::CapacityUnknown`](crate::batteries::passport_scope::PassportScope::CapacityUnknown)
//! and the opposite arithmetic. There the *obligation* turns on a number, so an
//! unstated number cannot exempt; here the *exemption* is what is stated, so an
//! unstated exemption does not exist. Both refuse to exempt on a missing field,
//! which is the property worth holding onto rather than the rule that produces it.
//!
//! ## What this does not answer
//!
//! Whether a passport should carry the index or its inputs at all. **The word
//! "passport" does not occur in either regulation** — the index belongs on the
//! label and in the product information sheet. That is a product question, and no
//! amount of further reading converts it into a legal one.
/// Whether Reg. (EU) 2023/1669's repairability index reaches a product.
///
/// Three outcomes rather than a `bool`. "This device type is outside the act"
/// and "this unit is carved out of it" are different sentences to put in front of
/// an operator, and they have different remedies — the first is never fixable,
/// the second is a declaration the operator made. Same reasoning as
/// [`PassportScope`](crate::batteries::passport_scope::PassportScope).
/// Whether `device_type` owes a repairability index under Reg. (EU) 2023/1669.
///
/// Both arguments are **wire** names — `"smartphone"`, `"tablet"`,
/// `"other-mobile-phone"`, `"cordless-phone"` and `"rollable-display"`,
/// `"high-security-communication"` — because this crate is `no_std` and
/// zero-dependency and is read by the Wasm product group plugins, which see JSON
/// and never the Rust enums. Matching is case-insensitive and trims surrounding
/// whitespace, as in [`crate::batteries::passport_scope`].
///
/// `exclusion` is `None` where the operator declared none, which means **not
/// excluded** — see the module documentation for why silence cannot grant a
/// carve-out. An unrecognised exclusion string is likewise not an exclusion: a
/// value this build does not know is not a value it may act on.
///
/// The device-type check runs **first**. A cordless phone declaring a rollable
/// display answers [`NotCovered`](RepairabilityIndexScope::NotCovered), because
/// the act never reached it and an exclusion from an act that does not apply is
/// not the reason to give an operator.