Skip to main content

dpp_domain/product_group/data/electronics/
repairability_index.rs

1//! The Annex IV point 5 inputs an operator declares, and the Art. 1 exclusion.
2//!
3//! ✅ COMPLIANCE-PIN: EU 2023/1669, Annex IV point 5 and Art. 1 (OJ L 214,
4//! 31.8.2023, pp. 12 and 26). Read from the Official Journal text.
5//!
6//! # What this is, and what it deliberately is not
7//!
8//! **Declared inputs, not a score.** Annex IV point 5 computes the index as
9//! `R = (SDD*0,25)+(SF*0,15)+(ST*0,15)+(SSP*0,15)+(SSU*0,15)+(SRI*0,15)` over
10//! six parameters, three of them scored per priority part. This module carries
11//! the six parameters as an operator states them. It does not compute `R`, and
12//! nothing here grades anything.
13//!
14//! That split is the useful shape and it is the one the Regulation itself
15//! describes: **Annex IX Table 10** sets a verification tolerance — *"The
16//! determined value shall not be more than 4 % lower than the declared
17//! value."* Declared by the supplier, re-determined by an authority. A passport
18//! carrying the inputs, and a receipt recomputing the index from them, is the
19//! same structure.
20//!
21//! # Why the shape is restated here rather than shared with the calculator
22//!
23//! The calculator's input type and this one describe the same six parameters and
24//! are answerable to different things. This is **passport content** — a set of
25//! values an operator declares, versioned with the electronics schema, and bound
26//! by the signature over the record. The calculator's is a function signature.
27//! Neither crate depends on the other by design, and that boundary is what keeps
28//! a change to a calculation from silently changing what a passport means.
29//!
30//! The cost is real: the two can drift, and only a consumer wiring them together
31//! would notice. Worth a shared home if a third consumer appears; not worth
32//! inverting the crate graph for a second.
33//!
34//! # No obligation attaches to any of this
35//!
36//! **The word "passport" does not appear in Reg. (EU) 2023/1669.** The index
37//! belongs on the energy label and in the product information sheet. Carrying it
38//! here is a product decision, not a duty being discharged, and this note exists
39//! so nobody later cites the Regulation for an obligation it does not create.
40
41use super::priority_part_scores::{MAX_SCORE, MIN_SCORE, PriorityPartScores};
42use serde::{Deserialize, Serialize};
43
44/// The complete Annex IV point 5 input set for one smartphone or slate tablet.
45///
46/// Three part-level parameters carrying per-part scores, and three product-level
47/// parameters that are single 1–5 scores. Every rubric below is Annex IV's.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct RepairabilityIndexDeclaration {
51    /// SDD — disassembly depth, scored per part from the number of steps:
52    /// ≤2 steps scores 5, >15 steps scores 1.
53    ///
54    /// Annex IV's counting rules apply, including the five-step penalty where
55    /// reassembly needs a remote serial-number authorisation.
56    pub disassembly_depth: PriorityPartScores,
57    /// SF — fasteners: reusable 5, resupplied 3, removable 1.
58    pub fasteners: PriorityPartScores,
59    /// ST — tools: none 5, basic 4, supplied with the spare part 3, supplied
60    /// with the product 2, commercially available 1.
61    pub tools: PriorityPartScores,
62    /// SSP — spare-part availability, product level, 1–5.
63    pub spare_parts: u8,
64    /// SSU — operating-system update duration, product level: ≥7 years scores 5,
65    /// 6 years scores 3, 5 years scores 1.
66    pub software_updates: u8,
67    /// SRI — repair-information availability, product level, 1–5.
68    pub repair_information: u8,
69}
70
71impl RepairabilityIndexDeclaration {
72    /// Whether the three part-level parameters agree on whether the product folds.
73    ///
74    /// Annex IV selects between two weight sets on the presence of the hinge
75    /// assembly, so a declaration scoring FM under one parameter and omitting it
76    /// under another does not describe any product the annex can grade. Checked
77    /// rather than assumed, because the failure is silent: the index would be
78    /// computed against whichever weight set the first parameter happened to
79    /// suggest.
80    #[must_use]
81    pub const fn foldable_is_consistent(&self) -> bool {
82        let a = self.disassembly_depth.is_foldable();
83        let b = self.fasteners.is_foldable();
84        let c = self.tools.is_foldable();
85        a == b && b == c
86    }
87
88    /// Whether every score, part-level and product-level, is within 1–5.
89    #[must_use]
90    pub fn scores_are_in_range(&self) -> bool {
91        let in_range = |s: u8| (MIN_SCORE..=MAX_SCORE).contains(&s);
92        self.disassembly_depth.scores_are_in_range()
93            && self.fasteners.scores_are_in_range()
94            && self.tools.scores_are_in_range()
95            && in_range(self.spare_parts)
96            && in_range(self.software_updates)
97            && in_range(self.repair_information)
98    }
99}