Skip to main content

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}