dpp_domain/domain/sector/data/textile.rs
1//! Textile (EU Textile DPP, expected ~Q2 2027 adoption, compliance ~2028–2029).
2
3use serde::{Deserialize, Serialize};
4
5use crate::domain::gtin::Gtin;
6
7use super::shared::SvhcSubstance;
8
9/// A single fibre entry in a textile product's composition list.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(rename_all = "camelCase")]
12pub struct FibreEntry {
13 /// Fibre name, e.g. `"cotton"`, `"polyester"`, `"recycled_polyester"`.
14 pub fibre: String,
15 /// Percentage by weight (0.0–100.0).
16 ///
17 /// All entries in the composition list must sum to approximately 100.0
18 /// (± 2.0 percentage point tolerance to accommodate rounding).
19 pub pct: f64,
20 /// ISO 3166-1 alpha-2 country code where this specific fibre was sourced.
21 ///
22 /// Per-fibre origin traceability as anticipated by the textile delegated act.
23 /// When present, allows downstream actors to verify sourcing per fibre rather
24 /// than relying on a single `country_of_raw_material_origin` for the whole product.
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub country_of_origin: Option<String>,
27}
28
29/// Textile-specific fields for EU textile DPP compliance.
30///
31/// Based on the anticipated EU Textile DPP delegated act (~Q2 2027 adoption).
32/// Fields marked as `Option` are optional under v1.0.0 but may become mandatory
33/// once the delegated act is finalised.
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35#[serde(rename_all = "camelCase")]
36pub struct TextileData {
37 // ── Mandatory fields (v1.0.0) ──────────────────────────────────────────
38 /// 14-digit GTIN identifying the textile product.
39 pub gtin: Gtin,
40 /// List of fibres and their percentage composition. Must sum to ~100%.
41 pub fibre_composition: Vec<FibreEntry>,
42 /// ISO 3166-1 alpha-2 country code where the textile was manufactured.
43 pub country_of_origin: String,
44 /// ISO 3758 care symbols or free text care instructions.
45 pub care_instructions: String,
46 /// Chemical compliance standard, e.g. `"OEKO-TEX 100"`, `"REACH"`, `"GOTS"`.
47 pub chemical_compliance_standard: String,
48
49 // ── Environmental metrics ──────────────────────────────────────────────
50 /// Total recycled content as a percentage of total weight (0.0–100.0).
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub recycled_content_pct: Option<f64>,
53 /// Carbon footprint in kg CO₂e per unit.
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub carbon_footprint_kg_co2e: Option<f64>,
56 /// Water consumption in litres per unit produced.
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub water_use_litres: Option<f64>,
59 /// Microplastic fibre shedding in milligrams per wash cycle.
60 ///
61 /// Measured per ISO/DIS 4484 or equivalent. Relevant for synthetic textiles
62 /// where microplastic release is a growing regulatory concern.
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub microplastic_shedding_mg_per_wash: Option<f64>,
65
66 // ── Durability & repairability ─────────────────────────────────────────
67 /// Repairability score (0.0–10.0) per EU methodology.
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub repair_score: Option<f64>,
70 /// Durability score (0.0–10.0) measuring resistance to pilling,
71 /// colour fastness, dimensional stability, and seam strength.
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub durability_score: Option<f64>,
74 /// Expected number of wash cycles before significant quality degradation.
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub expected_wash_cycles: Option<u32>,
77
78 // ── Traceability ───────────────────────────────────────────────────────
79 /// ISO 3166-1 alpha-2 country of raw material origin (product-level fallback).
80 /// Per-fibre origin on `FibreEntry.country_of_origin` takes precedence when present.
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub country_of_raw_material_origin: Option<String>,
83
84 // ── SCIP / SVHC substance disclosure ───────────────────────────────────
85 /// List of SVHC substances present above the 0.1% w/w threshold.
86 ///
87 /// Required by REACH Article 33 and linked to the ECHA SCIP database.
88 /// An empty vec means the manufacturer has checked and found no SVHCs.
89 /// `None` means the check has not yet been performed.
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub svhc_substances: Option<Vec<SvhcSubstance>>,
92
93 // ── Substances of concern (beyond SVHC) ──────────────────────────────
94 /// Allergens or sensitising substances present in the textile.
95 /// Covers contact allergens regulated under REACH Annex XVII entry 72
96 /// (e.g. certain disperse dyes, chromium VI, nickel in accessories).
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub allergens: Option<Vec<String>>,
99
100 /// Restricted substances present that are not classified as SVHC but are
101 /// regulated under sector-specific rules (e.g. PFASs, flame retardants,
102 /// biocides). Each entry is a free-text substance identifier.
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub substances_of_concern: Option<Vec<String>>,
105
106 // ── Circularity & end-of-life ─────────────────────────────────────────
107 /// Recyclability classification, e.g. `"mono-material"`, `"multi-material"`,
108 /// `"not-recyclable"`. Based on design-for-recycling assessment.
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub recyclability_class: Option<String>,
111
112 /// End-of-life instructions: how to dispose of or recycle the product.
113 /// Free text or URL pointing to take-back/collection point information.
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub end_of_life_instructions: Option<String>,
116
117 /// Whether the product has been refurbished or reused, and if so its
118 /// condition grade (e.g. `"like-new"`, `"good"`, `"fair"`).
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub reuse_condition: Option<String>,
121
122 /// Number of prior ownership cycles (0 for new products).
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub prior_use_cycles: Option<u32>,
125
126 // ── Restricted data (gated by access control) ──────────────────────────
127 /// Free-text or structured disassembly / deconstruction instructions.
128 /// Classified `Disclosure::Restricted` (Verifiable Credential required).
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub disassembly_instructions: Option<String>,
131 /// Whether spare parts or replacement components are available from the manufacturer.
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub spare_parts_available: Option<bool>,
134 /// Product net weight in grams (used for per-unit environmental calculations).
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub product_weight_grams: Option<f64>,
137
138 // ── Repair & maintenance history ──────────────────────────────────────
139 /// URL or structured log of repair events performed on this specific item.
140 /// Relevant for products sold with a repair history (e.g. certified
141 /// pre-owned programmes).
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub repair_history_url: Option<String>,
144
145 /// Number of completed professional repairs on this item.
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub repair_count: Option<u32>,
148
149 /// Environmental impact score per garment lifecycle (PEF/OEF methodology).
150 /// Dimensionless single score aggregating multiple impact categories.
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub pef_score: Option<f64>,
153}