Skip to main content

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}