1pub mod error;
16pub mod holder;
17pub mod matcher;
18pub mod presenter;
19pub mod receive;
20pub mod store;
21pub mod wallet;
22
23use serde::{Deserialize, Serialize};
24
25use baseid_anoncreds::AnonCredsCredential;
26use baseid_bbs::BbsCredential;
27use baseid_core::credential::{CredentialMetadata, CredentialSummary};
28use baseid_core::types::{CredentialFormat, CredentialId};
29use baseid_vc::credential::Issuer;
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub enum AnyCredential {
34 W3cVc(baseid_vc::VerifiableCredential),
36 Mdoc(baseid_mdl::MobileDocument),
38 SdJwtVc(baseid_sd_jwt::SdJwt),
40 Bbs(BbsCredential),
42 AnonCreds(AnonCredsCredential),
44}
45
46impl AnyCredential {
47 pub fn credential_format(&self) -> CredentialFormat {
49 match self {
50 AnyCredential::W3cVc(_) => CredentialFormat::W3cVc,
51 AnyCredential::Mdoc(_) => CredentialFormat::Mdl,
52 AnyCredential::SdJwtVc(_) => CredentialFormat::SdJwtVc,
53 AnyCredential::Bbs(_) => CredentialFormat::Bbs,
54 AnyCredential::AnonCreds(_) => CredentialFormat::AnonCreds,
55 }
56 }
57
58 pub fn issuer_id(&self) -> String {
60 match self {
61 AnyCredential::W3cVc(vc) => match &vc.issuer {
62 Issuer::Uri(uri) => uri.clone(),
63 Issuer::Object { id, .. } => id.clone(),
64 },
65 AnyCredential::Mdoc(_) => "mdoc-issuer".to_string(), AnyCredential::SdJwtVc(_) => "sd-jwt-issuer".to_string(), AnyCredential::Bbs(cred) => cred.issuer.clone(),
68 AnyCredential::AnonCreds(cred) => cred.issuer_did.clone(),
69 }
70 }
71
72 pub fn label(&self) -> String {
74 match self {
75 AnyCredential::W3cVc(vc) => {
76 let types: Vec<&str> = vc
77 .r#type
78 .iter()
79 .filter(|t| *t != "VerifiableCredential")
80 .map(|s| s.as_str())
81 .collect();
82 if types.is_empty() {
83 "Verifiable Credential".to_string()
84 } else {
85 types.join(", ")
86 }
87 }
88 AnyCredential::Mdoc(_) => "Mobile Document".to_string(),
89 AnyCredential::SdJwtVc(_) => "SD-JWT Credential".to_string(),
90 AnyCredential::Bbs(cred) => {
91 let types: Vec<&str> = cred
92 .types
93 .iter()
94 .filter(|t| *t != "VerifiableCredential")
95 .map(|s| s.as_str())
96 .collect();
97 if types.is_empty() {
98 "BBS+ Credential".to_string()
99 } else {
100 types[0].to_string()
101 }
102 }
103 AnyCredential::AnonCreds(cred) => {
104 let vc = cred.to_w3c_vc();
105 let types: Vec<&str> = vc
106 .r#type
107 .iter()
108 .filter(|t| *t != "VerifiableCredential")
109 .map(|s| s.as_str())
110 .collect();
111 if types.is_empty() {
112 "AnonCreds Credential".to_string()
113 } else {
114 types[0].to_string()
115 }
116 }
117 }
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct CredentialRecord {
124 pub id: CredentialId,
126 pub credential: AnyCredential,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub raw: Option<String>,
131}
132
133impl CredentialRecord {
134 pub fn metadata(&self) -> CredentialMetadata {
136 let (issuance_date, expiration_date) = match &self.credential {
137 AnyCredential::W3cVc(vc) => (
138 vc.valid_from.clone().unwrap_or_default(),
139 vc.valid_until.clone(),
140 ),
141 AnyCredential::Mdoc(_) => (String::new(), None),
142 AnyCredential::SdJwtVc(_) => (String::new(), None),
143 AnyCredential::Bbs(_) => (String::new(), None),
144 AnyCredential::AnonCreds(_) => (String::new(), None),
145 };
146
147 CredentialMetadata {
148 id: self.id.clone(),
149 format: self.credential.credential_format(),
150 issuer: self.credential.issuer_id(),
151 issuance_date,
152 expiration_date,
153 }
154 }
155
156 pub fn summary(&self) -> CredentialSummary {
158 CredentialSummary {
159 metadata: self.metadata(),
160 label: self.credential.label(),
161 }
162 }
163}
164
165#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct CredentialFilter {
168 pub format: Option<CredentialFormat>,
170 pub issuer: Option<String>,
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use baseid_vc::credential::Issuer;
178 use baseid_vc::VerifiableCredential;
179
180 fn sample_vc() -> VerifiableCredential {
181 VerifiableCredential {
182 context: vec!["https://www.w3.org/ns/credentials/v2".to_string()],
183 id: Some("urn:uuid:test".to_string()),
184 r#type: vec![
185 "VerifiableCredential".to_string(),
186 "UniversityDegreeCredential".to_string(),
187 ],
188 issuer: Issuer::Uri("did:key:z6Mk...".to_string()),
189 valid_from: Some("2024-01-01T00:00:00Z".to_string()),
190 valid_until: Some("2030-01-01T00:00:00Z".to_string()),
191 credential_subject: serde_json::json!({"id": "did:key:holder"}),
192 credential_status: None,
193 proof: None,
194 }
195 }
196
197 #[test]
198 fn any_credential_format_w3c_vc() {
199 let cred = AnyCredential::W3cVc(sample_vc());
200 assert_eq!(cred.credential_format(), CredentialFormat::W3cVc);
201 }
202
203 #[test]
204 fn any_credential_issuer_id_uri() {
205 let cred = AnyCredential::W3cVc(sample_vc());
206 assert_eq!(cred.issuer_id(), "did:key:z6Mk...");
207 }
208
209 #[test]
210 fn any_credential_issuer_id_object() {
211 let mut vc = sample_vc();
212 vc.issuer = Issuer::Object {
213 id: "did:web:example.com".to_string(),
214 properties: serde_json::Map::new(),
215 };
216 let cred = AnyCredential::W3cVc(vc);
217 assert_eq!(cred.issuer_id(), "did:web:example.com");
218 }
219
220 #[test]
221 fn any_credential_label() {
222 let cred = AnyCredential::W3cVc(sample_vc());
223 assert_eq!(cred.label(), "UniversityDegreeCredential");
224 }
225
226 #[test]
227 fn credential_record_metadata() {
228 let record = CredentialRecord {
229 id: CredentialId("cred-1".to_string()),
230 credential: AnyCredential::W3cVc(sample_vc()),
231 raw: None,
232 };
233 let meta = record.metadata();
234 assert_eq!(meta.id, CredentialId("cred-1".to_string()));
235 assert_eq!(meta.format, CredentialFormat::W3cVc);
236 assert_eq!(meta.issuer, "did:key:z6Mk...");
237 assert_eq!(meta.issuance_date, "2024-01-01T00:00:00Z");
238 assert_eq!(
239 meta.expiration_date.as_deref(),
240 Some("2030-01-01T00:00:00Z")
241 );
242 }
243
244 #[test]
245 fn credential_record_summary() {
246 let record = CredentialRecord {
247 id: CredentialId("cred-1".to_string()),
248 credential: AnyCredential::W3cVc(sample_vc()),
249 raw: None,
250 };
251 let summary = record.summary();
252 assert_eq!(summary.label, "UniversityDegreeCredential");
253 assert_eq!(summary.metadata.format, CredentialFormat::W3cVc);
254 }
255}