dpp_domain/operator/responsible.rs
1//! [`ResponsibleOperator`] and its [`OperatorRole`].
2
3use serde::{Deserialize, Serialize};
4
5/// Identifies an economic operator responsible for a DPP.
6///
7/// Under ESPR, the "responsible economic operator" is whoever places or
8/// makes the product available on the EU market. This can be the original
9/// manufacturer, an importer, a distributor, or a remanufacturer.
10///
11/// # Contact details
12///
13/// [`postal_address`](Self::postal_address),
14/// [`electronic_address`](Self::electronic_address) and
15/// [`registered_trade_name`](Self::registered_trade_name) exist because four
16/// separate provisions require the same four-part shape of whoever is
17/// answerable for a product, and confirmed against the verbatim OJ text they
18/// are: **ESPR Art. 27(6)** (manufacturer) and **Art. 29(3)** (importer) —
19/// "name, registered trade name or registered trade mark, postal address at
20/// which, and electronic means of communication through which, they can be
21/// contacted"; **Art. 4(4) of Regulation (EU) 2019/1020** — "the name,
22/// registered trade name or registered trade mark, and contact details,
23/// including the postal address"; and **Art. 16(3) of Regulation (EU)
24/// 2023/988** — the same, "including the postal and electronic address".
25///
26/// **ESPR Annex III, point (k)** is what puts them in the passport: "the name,
27/// contact details and unique operator identifier of the economic operator
28/// established in the Union responsible for carrying out the tasks set out in
29/// Article 4 of Regulation (EU) 2019/1020 or Article 15 of Regulation (EU)
30/// 2023/988, or similar tasks pursuant to other Union law applicable to the
31/// product". Contact details are never a free string in any of the four.
32///
33/// ⚠️ **That quote's "Article 15" is the Regulation's own cross-reference, and
34/// it points at the wrong article** — Art. 15 of 2023/988 is *Cooperation of
35/// economic operators with market surveillance authorities*; the
36/// responsible-person provision is Art. 16. The quote is left exactly as
37/// enacted, and
38/// [`ResponsibilityBasis::GeneralProductSafety`](crate::operator::ResponsibilityBasis::GeneralProductSafety)
39/// carries the explanation, including why it changes nothing in substance.
40///
41/// All three are `Option` because this type is a persisted shape — it is stored
42/// inside the transfer chain — and the envelope rule is additive-only. `None`
43/// means not stated, never "has none".
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct ResponsibleOperator {
47 /// The operator's DID (e.g. `did:web:acme.example.com`).
48 pub did: String,
49 /// Human-readable name of the economic operator.
50 pub name: String,
51 /// The operator's role in the supply chain.
52 pub role: OperatorRole,
53 /// EU-assigned economic operator identifier, if available.
54 pub eu_operator_id: Option<String>,
55 /// The scheme [`Self::eu_operator_id`] is expressed in — `"vat"`, `"lei"`,
56 /// `"eori"`, `"duns"`. `None` when no EU identifier is held.
57 ///
58 /// Paired with the value because an identifier without its scheme cannot be
59 /// stated truthfully to a registry: the value alone does not say what it is,
60 /// and guessing produces a false claim rather than a missing one.
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub eu_operator_id_scheme: Option<String>,
63 /// ISO 3166-1 alpha-2 country code of the operator's establishment.
64 pub country: String,
65 /// Registered trade name or registered trade mark, where it differs from
66 /// [`name`](Self::name).
67 ///
68 /// All four provisions name it as an alternative to the legal name — "name,
69 /// registered trade name or registered trade mark" — because the two are
70 /// frequently different and a reader holding the product sees the trading
71 /// one. Kept as a separate field rather than overloading `name`, which is
72 /// the legal name.
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub registered_trade_name: Option<String>,
75 /// Postal address at which the operator can be contacted.
76 ///
77 /// ESPR Art. 27(6) adds a constraint the other three do not: "The address
78 /// shall indicate a single point where the manufacturer can be contacted."
79 /// That is a property of the content, not of the shape, so nothing here
80 /// enforces it — a free string cannot express "single point", and a
81 /// structured address still could not say whether it is the only one.
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub postal_address: Option<String>,
84 /// Electronic means of communication through which the operator can be
85 /// contacted — an email address, or a contact URL.
86 ///
87 /// Distinct from [`did`](Self::did), and deliberately: a DID document
88 /// resolves keys. Nothing in it is required to be a mailbox or a form, so it
89 /// is not a channel through which a person can be contacted, and reading it
90 /// as one would satisfy the obligation on paper and not in fact.
91 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub electronic_address: Option<String>,
93}
94
95/// The role of an economic operator in the DPP supply chain.
96///
97/// Determines what DPP fields the operator may introduce or update,
98/// as specified by the applicable delegated act.
99///
100/// # Wider than Art. 4(2), on purpose
101///
102/// **Art. 4(2) of Regulation (EU) 2019/1020** defines a closed set of four who
103/// may be *the* responsible operator: a manufacturer established in the Union;
104/// an importer, where the manufacturer is not; an authorised representative
105/// with a written mandate; and a fulfilment service provider established in the
106/// Union, where no other is. This enum holds those four and five more, because
107/// it also describes who a passport can be *transferred to* — a remanufacturer
108/// or a preparer for reuse takes over responsibility without being an Art. 4(2)
109/// designation, and a distributor appears in the chain while never being able
110/// to be the responsible operator at all.
111///
112/// [`ResponsibilityBasis`](super::ResponsibilityBasis) is what narrows the set
113/// where the narrowing matters.
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116#[non_exhaustive]
117pub enum OperatorRole {
118 /// Original equipment manufacturer.
119 Manufacturer,
120 /// Imports the product into the EU market.
121 Importer,
122 /// Makes the product available on the market without altering it.
123 Distributor,
124 /// An EU-established entity authorised to act on behalf of a
125 /// non-EU manufacturer.
126 AuthorisedRepresentative,
127 /// An EU-established provider of warehousing, packaging, addressing or
128 /// dispatching for products it does not own.
129 ///
130 /// Art. 4(2)(d) of Regulation (EU) 2019/1020 makes this operator the
131 /// responsible one **only** "where no other economic operator as mentioned
132 /// in points (a), (b) and (c) is established in the Union" — it is the
133 /// fallback that stops a product having no answerable party at all. Missing
134 /// from this enum until 2026-09-07, which left that case unexpressible.
135 FulfilmentServiceProvider,
136 /// Performs remanufacturing — restores the product to original
137 /// or improved specifications.
138 Remanufacturer,
139 /// Adapts the product for a different purpose than originally intended.
140 Repurposer,
141 /// Prepares a used product for resale (testing, cleaning, repair).
142 PreparerForReuse,
143 /// Professional repairer with authorised DPP update rights.
144 Repairer,
145 /// Processes end-of-life products for material recovery.
146 Recycler,
147}
148
149impl OperatorRole {
150 /// Every role this build models, for exhaustive iteration.
151 ///
152 /// `OperatorRole` is `#[non_exhaustive]`, so a consumer outside this crate
153 /// cannot enumerate it, and one publishing an API description has to. See
154 /// [`crate::seal::SealFormat::ALL`] for the same contract: a role
155 /// added later is deliberately not covered until it is added here.
156 pub const ALL: &'static [Self] = &[
157 Self::Manufacturer,
158 Self::Importer,
159 Self::Distributor,
160 Self::AuthorisedRepresentative,
161 Self::FulfilmentServiceProvider,
162 Self::Remanufacturer,
163 Self::Repurposer,
164 Self::PreparerForReuse,
165 Self::Repairer,
166 Self::Recycler,
167 ];
168
169 /// Whether this role can be the responsible operator under Art. 4(2) of
170 /// Regulation (EU) 2019/1020, which names four and only four.
171 ///
172 /// Advisory rather than enforced: (a) and (b) carry conditions this type
173 /// cannot see — "established in the Union", "where the manufacturer is not
174 /// established in the Union" — and (d) is conditional on the absence of the
175 /// other three. So a `true` here means "not excluded by role", never "is the
176 /// Art. 4(2) operator".
177 #[must_use]
178 pub const fn can_be_art_4_operator(&self) -> bool {
179 matches!(
180 self,
181 Self::Manufacturer
182 | Self::Importer
183 | Self::AuthorisedRepresentative
184 | Self::FulfilmentServiceProvider
185 )
186 }
187}