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
//! Textile (EU Textile DPP, expected ~Q2 2027 adoption, compliance ~2028–2029).
use serde::{Deserialize, Serialize};
use super::shared::SvhcSubstance;
/// A single fibre entry in a textile product's composition list.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FibreEntry {
/// Fibre name, e.g. `"cotton"`, `"polyester"`, `"recycled_polyester"`.
pub fibre: String,
/// Percentage by weight (0.0–100.0).
///
/// All entries in the composition list must sum to approximately 100.0
/// (± 2.0 percentage point tolerance to accommodate rounding).
pub pct: f64,
/// ISO 3166-1 alpha-2 country code where this specific fibre was sourced.
///
/// Per-fibre origin traceability as anticipated by the textile delegated act.
/// When present, allows downstream actors to verify sourcing per fibre rather
/// than relying on a single `country_of_raw_material_origin` for the whole product.
#[serde(skip_serializing_if = "Option::is_none")]
pub country_of_origin: Option<String>,
}
/// Textile-specific fields for EU textile DPP compliance.
///
/// Based on the anticipated EU Textile DPP delegated act (~Q2 2027 adoption).
/// Fields marked as `Option` are optional under v1.0.0 but may become mandatory
/// once the delegated act is finalised.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TextileData {
// ── Mandatory fields (v1.0.0) ──────────────────────────────────────────
/// 14-digit GTIN identifying the textile product.
pub gtin: String,
/// List of fibres and their percentage composition. Must sum to ~100%.
pub fibre_composition: Vec<FibreEntry>,
/// ISO 3166-1 alpha-2 country code where the textile was manufactured.
pub country_of_manufacturing: String,
/// ISO 3758 care symbols or free text care instructions.
pub care_instructions: String,
/// Chemical compliance standard, e.g. `"OEKO-TEX 100"`, `"REACH"`, `"GOTS"`.
pub chemical_compliance_standard: String,
// ── Environmental metrics ──────────────────────────────────────────────
/// Total recycled content as a percentage of total weight (0.0–100.0).
#[serde(skip_serializing_if = "Option::is_none")]
pub recycled_content_pct: Option<f64>,
/// Carbon footprint in kg CO₂e per unit.
#[serde(skip_serializing_if = "Option::is_none")]
pub carbon_footprint_kg_co2e: Option<f64>,
/// Water consumption in litres per unit produced.
#[serde(skip_serializing_if = "Option::is_none")]
pub water_use_litres: Option<f64>,
/// Microplastic fibre shedding in milligrams per wash cycle.
///
/// Measured per ISO/DIS 4484 or equivalent. Relevant for synthetic textiles
/// where microplastic release is a growing regulatory concern.
#[serde(skip_serializing_if = "Option::is_none")]
pub microplastic_shedding_mg_per_wash: Option<f64>,
// ── Durability & repairability ─────────────────────────────────────────
/// Repairability score (0.0–10.0) per EU methodology.
#[serde(skip_serializing_if = "Option::is_none")]
pub repair_score: Option<f64>,
/// Durability score (0.0–10.0) measuring resistance to pilling,
/// colour fastness, dimensional stability, and seam strength.
#[serde(skip_serializing_if = "Option::is_none")]
pub durability_score: Option<f64>,
/// Expected number of wash cycles before significant quality degradation.
#[serde(skip_serializing_if = "Option::is_none")]
pub expected_wash_cycles: Option<u32>,
// ── Traceability ───────────────────────────────────────────────────────
/// ISO 3166-1 alpha-2 country of raw material origin (product-level fallback).
/// Per-fibre origin on `FibreEntry.country_of_origin` takes precedence when present.
#[serde(skip_serializing_if = "Option::is_none")]
pub country_of_raw_material_origin: Option<String>,
// ── SCIP / SVHC substance disclosure ───────────────────────────────────
/// List of SVHC substances present above the 0.1% w/w threshold.
///
/// Required by REACH Article 33 and linked to the ECHA SCIP database.
/// An empty vec means the manufacturer has checked and found no SVHCs.
/// `None` means the check has not yet been performed.
#[serde(skip_serializing_if = "Option::is_none")]
pub svhc_substances: Option<Vec<SvhcSubstance>>,
// ── Substances of concern (beyond SVHC) ──────────────────────────────
/// Allergens or sensitising substances present in the textile.
/// Covers contact allergens regulated under REACH Annex XVII entry 72
/// (e.g. certain disperse dyes, chromium VI, nickel in accessories).
#[serde(skip_serializing_if = "Option::is_none")]
pub allergens: Option<Vec<String>>,
/// Restricted substances present that are not classified as SVHC but are
/// regulated under sector-specific rules (e.g. PFASs, flame retardants,
/// biocides). Each entry is a free-text substance identifier.
#[serde(skip_serializing_if = "Option::is_none")]
pub substances_of_concern: Option<Vec<String>>,
// ── Circularity & end-of-life ─────────────────────────────────────────
/// Recyclability classification, e.g. `"mono-material"`, `"multi-material"`,
/// `"not-recyclable"`. Based on design-for-recycling assessment.
#[serde(skip_serializing_if = "Option::is_none")]
pub recyclability_class: Option<String>,
/// End-of-life instructions: how to dispose of or recycle the product.
/// Free text or URL pointing to take-back/collection point information.
#[serde(skip_serializing_if = "Option::is_none")]
pub end_of_life_instructions: Option<String>,
/// Whether the product has been refurbished or reused, and if so its
/// condition grade (e.g. `"like-new"`, `"good"`, `"fair"`).
#[serde(skip_serializing_if = "Option::is_none")]
pub reuse_condition: Option<String>,
/// Number of prior ownership cycles (0 for new products).
#[serde(skip_serializing_if = "Option::is_none")]
pub prior_use_cycles: Option<u32>,
// ── Professional-tier data (gated by access control) ───────────────────
/// Free-text or structured disassembly / deconstruction instructions.
/// Gated behind the Professional access tier (Verifiable Credential required).
#[serde(skip_serializing_if = "Option::is_none")]
pub disassembly_instructions: Option<String>,
/// Whether spare parts or replacement components are available from the manufacturer.
#[serde(skip_serializing_if = "Option::is_none")]
pub spare_parts_available: Option<bool>,
/// Product net weight in grams (used for per-unit environmental calculations).
#[serde(skip_serializing_if = "Option::is_none")]
pub product_weight_grams: Option<f64>,
// ── Repair & maintenance history ──────────────────────────────────────
/// URL or structured log of repair events performed on this specific item.
/// Relevant for products sold with a repair history (e.g. certified
/// pre-owned programmes).
#[serde(skip_serializing_if = "Option::is_none")]
pub repair_history_url: Option<String>,
/// Number of completed professional repairs on this item.
#[serde(skip_serializing_if = "Option::is_none")]
pub repair_count: Option<u32>,
/// Environmental impact score per garment lifecycle (PEF/OEF methodology).
/// Dimensionless single score aggregating multiple impact categories.
#[serde(skip_serializing_if = "Option::is_none")]
pub pef_score: Option<f64>,
}