dpp_vc/credential/types.rs
1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4/// Re-export the canonical access vocabulary from dpp-domain.
5pub use dpp_domain::Audience;
6
7// ─── Credential role ─────────────────────────────────────────────────────────
8
9/// The access role granted by a Verifiable Credential.
10///
11/// Maps an operator role to the [`Audience`] it may claim, alongside the
12/// specific operator roles defined in the transfer-of-responsibility model.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum CredentialRole {
16 /// Authorised repairer — can access disassembly instructions, spare parts info.
17 AuthorisedRepairer,
18 /// Recycler — can access material composition, SVHC data, recycling instructions.
19 Recycler,
20 /// Remanufacturer — can access full technical specifications.
21 Remanufacturer,
22 /// Preparer for reuse — can access quality and safety data.
23 PreparerForReuse,
24 /// Distributor holding a legitimate interest.
25 Distributor,
26 /// Market surveillance authority — Annex XIII points 1, 2 and 3. Note this
27 /// does **not** include point 4 individual-item data.
28 MarketSurveillanceAuthority,
29 /// Customs authority — access for border control.
30 CustomsAuthority,
31 /// Notified body — conformity assessment.
32 NotifiedBody,
33 /// Custom role (extension point for sector-specific roles).
34 Custom(String),
35}
36
37impl CredentialRole {
38 /// The Art. 77(2) audience this role belongs to.
39 ///
40 /// Note the consequence of the lattice: an authority does **not** thereby
41 /// gain the individual-item data of Annex XIII point 4, which Art. 77(2)(b)
42 /// does not grant it. A market surveillance authority that also needs that
43 /// data needs a separate legitimate-interest basis for it.
44 #[must_use]
45 pub fn audience(&self) -> Audience {
46 match self {
47 Self::MarketSurveillanceAuthority | Self::CustomsAuthority | Self::NotifiedBody => {
48 Audience::Authority
49 }
50 _ => Audience::LegitimateInterest,
51 }
52 }
53}
54
55// ─── Credential subject ─────────────────────────────────────────────────────
56
57/// The claims inside a DPP access credential.
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59#[serde(rename_all = "camelCase")]
60pub struct DppCredentialSubject {
61 /// DID of the credential holder (the entity being granted access).
62 pub id: String,
63 /// Legal name of the credential holder.
64 pub name: String,
65 /// The role being granted.
66 pub role: CredentialRole,
67 /// ISO 3166-1 alpha-2 country code of the holder's registration.
68 pub country: String,
69 /// Sector(s) this credential applies to (e.g., `["textile"]`).
70 /// Empty means all sectors.
71 #[serde(skip_serializing_if = "Vec::is_empty", default)]
72 pub sectors: Vec<String>,
73 /// Specific product categories this credential covers.
74 /// Empty means all categories within the sectors.
75 #[serde(skip_serializing_if = "Vec::is_empty", default)]
76 pub product_categories: Vec<String>,
77}
78
79// ─── Verifiable Credential envelope ─────────────────────────────────────────
80
81/// A W3C Verifiable Credential for DPP access.
82///
83/// Follows the VC Data Model v2.0 structure with DPP-specific extensions.
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
85#[serde(rename_all = "camelCase")]
86pub struct DppAccessCredential {
87 /// JSON-LD context (always includes the VC context).
88 #[serde(rename = "@context")]
89 pub context: Vec<String>,
90 /// Credential type (always includes "VerifiableCredential").
91 #[serde(rename = "type")]
92 pub credential_type: Vec<String>,
93 /// Unique credential ID (UUID-based URI).
94 pub id: String,
95 /// DID of the issuer (the authority granting access).
96 pub issuer: String,
97 /// When the credential was issued.
98 pub valid_from: DateTime<Utc>,
99 /// When the credential expires (mandatory for DPP credentials).
100 pub valid_until: DateTime<Utc>,
101 /// The access claims.
102 pub credential_subject: DppCredentialSubject,
103 /// Credential status for revocation checking.
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub credential_status: Option<CredentialStatus>,
106}
107
108/// Credential status descriptor for revocation checking.
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110#[serde(rename_all = "camelCase")]
111pub struct CredentialStatus {
112 /// URL to check revocation status.
113 pub id: String,
114 /// Status method type. Per W3C Bitstring Status List v1.0 this is
115 /// `"BitstringStatusListEntry"` (the older `"StatusList2021Entry"` is dated).
116 #[serde(rename = "type")]
117 pub status_type: String,
118 /// Index in the status list.
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub status_list_index: Option<String>,
121 /// URL of the status list credential.
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub status_list_credential: Option<String>,
124}