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