1use crate::{
6 CredentialSubject, CredentialSubjectBasic, CredentialSubjectEndorsement,
7 CredentialSubjectRCard, CredentialSubjectWitness, DTGCommon, DTGCredential, DTGCredentialType,
8 WitnessContext,
9};
10use chrono::{DateTime, Utc};
11use serde_json::Value;
12
13impl DTGCredential {
14 pub fn new_vmc(
22 issuer: String,
23 subject: String,
24 valid_from: DateTime<Utc>,
25 valid_until: Option<DateTime<Utc>>,
26 personhood: bool,
27 ) -> Self {
28 let mut vmc = DTGCommon {
29 issuer,
30 valid_from,
31 valid_until,
32 credential_subject: CredentialSubject::Basic(CredentialSubjectBasic { id: subject }),
33 ..Default::default()
34 };
35
36 vmc.type_.push(DTGCredentialType::Membership.to_string());
37
38 if personhood {
39 vmc.type_.push("PersonhoodCredential".to_string());
40 }
41
42 DTGCredential {
43 credential: vmc,
44 type_: DTGCredentialType::Membership,
45 version: crate::W3CVCVersion::V2_0,
46 }
47 }
48
49 pub fn new_vrc(
55 issuer: String,
56 subject: String,
57 valid_from: DateTime<Utc>,
58 valid_until: Option<DateTime<Utc>>,
59 ) -> Self {
60 let mut vrc = DTGCommon {
61 issuer,
62 valid_from,
63 valid_until,
64 credential_subject: CredentialSubject::Basic(CredentialSubjectBasic { id: subject }),
65 ..Default::default()
66 };
67
68 vrc.type_.push(DTGCredentialType::Relationship.to_string());
69
70 DTGCredential {
71 credential: vrc,
72 type_: DTGCredentialType::Relationship,
73 version: crate::W3CVCVersion::V2_0,
74 }
75 }
76
77 pub fn new_vic(
83 issuer: String,
84 subject: String,
85 valid_from: DateTime<Utc>,
86 valid_until: Option<DateTime<Utc>>,
87 ) -> Self {
88 let mut vic = DTGCommon {
89 issuer,
90 valid_from,
91 valid_until,
92 credential_subject: CredentialSubject::Basic(CredentialSubjectBasic { id: subject }),
93 ..Default::default()
94 };
95
96 vic.type_.push(DTGCredentialType::Invitation.to_string());
97
98 DTGCredential {
99 credential: vic,
100 type_: DTGCredentialType::Invitation,
101 version: crate::W3CVCVersion::V2_0,
102 }
103 }
104
105 pub fn new_vpc(
111 issuer: String,
112 subject: String,
113 valid_from: DateTime<Utc>,
114 valid_until: Option<DateTime<Utc>>,
115 ) -> Self {
116 let mut vpc = DTGCommon {
117 issuer,
118 valid_from,
119 valid_until,
120 credential_subject: CredentialSubject::Basic(CredentialSubjectBasic { id: subject }),
121 ..Default::default()
122 };
123
124 vpc.type_.push(DTGCredentialType::Persona.to_string());
125
126 DTGCredential {
127 credential: vpc,
128 type_: DTGCredentialType::Persona,
129 version: crate::W3CVCVersion::V2_0,
130 }
131 }
132
133 pub fn new_vec(
140 issuer: String,
141 subject: String,
142 valid_from: DateTime<Utc>,
143 valid_until: Option<DateTime<Utc>>,
144 endorsement: Value,
145 ) -> Self {
146 let mut vec = DTGCommon {
147 issuer,
148 valid_from,
149 valid_until,
150 credential_subject: CredentialSubject::Endorsement(CredentialSubjectEndorsement {
151 id: subject,
152 endorsement,
153 }),
154 ..Default::default()
155 };
156
157 vec.type_.push(DTGCredentialType::Endorsement.to_string());
158
159 DTGCredential {
160 credential: vec,
161 type_: DTGCredentialType::Endorsement,
162 version: crate::W3CVCVersion::V2_0,
163 }
164 }
165
166 pub fn new_vwc(
174 issuer: String,
175 subject: String,
176 valid_from: DateTime<Utc>,
177 valid_until: Option<DateTime<Utc>>,
178 digest: Option<String>,
179 witness_context: Option<WitnessContext>,
180 ) -> Self {
181 let mut vwc = DTGCommon {
182 issuer,
183 valid_from,
184 valid_until,
185 credential_subject: CredentialSubject::Witness(CredentialSubjectWitness {
186 id: subject,
187 digest,
188 witness_context,
189 }),
190 ..Default::default()
191 };
192
193 vwc.type_.push(DTGCredentialType::Witness.to_string());
194
195 DTGCredential {
196 credential: vwc,
197 type_: DTGCredentialType::Witness,
198 version: crate::W3CVCVersion::V2_0,
199 }
200 }
201
202 pub fn new_rcard(
209 issuer: String,
210 subject: String,
211 valid_from: DateTime<Utc>,
212 valid_until: Option<DateTime<Utc>>,
213 card: Value,
214 ) -> Self {
215 let mut rcard = DTGCommon {
216 issuer,
217 valid_from,
218 valid_until,
219 credential_subject: CredentialSubject::RCard(CredentialSubjectRCard {
220 id: subject,
221 card,
222 }),
223 ..Default::default()
224 };
225
226 rcard.type_.push(DTGCredentialType::RCard.to_string());
227
228 DTGCredential {
229 credential: rcard,
230 type_: DTGCredentialType::RCard,
231 version: crate::W3CVCVersion::V2_0,
232 }
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use crate::{DTGCredential, WitnessContext};
239 use chrono::{DateTime, Utc};
240 use serde_json::json;
241
242 #[test]
243 fn test_vmc_serialization() {
244 let vmc = DTGCredential::new_vmc(
245 "did:example:issuer".to_string(),
246 "did:example:subject".to_string(),
247 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
248 .unwrap()
249 .with_timezone(&Utc),
250 None,
251 false,
252 );
253
254 let txt = serde_json::to_string_pretty(&vmc).unwrap();
255 let sample = r#"{
256 "@context": [
257 "https://www.w3.org/ns/credentials/v2",
258 "https://firstperson.network/credentials/dtg/v1"
259 ],
260 "type": [
261 "VerifiableCredential",
262 "DTGCredential",
263 "MembershipCredential"
264 ],
265 "issuer": "did:example:issuer",
266 "validFrom": "2025-12-11T00:00:00Z",
267 "credentialSubject": {
268 "id": "did:example:subject"
269 }
270}"#;
271
272 assert_eq!(txt, sample);
273 }
274
275 #[test]
276 fn test_vmc_phc_serialization() {
277 let vmc = DTGCredential::new_vmc(
278 "did:example:issuer".to_string(),
279 "did:example:subject".to_string(),
280 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
281 .unwrap()
282 .with_timezone(&Utc),
283 None,
284 true,
285 );
286
287 let txt = serde_json::to_string_pretty(&vmc).unwrap();
288 let sample = r#"{
289 "@context": [
290 "https://www.w3.org/ns/credentials/v2",
291 "https://firstperson.network/credentials/dtg/v1"
292 ],
293 "type": [
294 "VerifiableCredential",
295 "DTGCredential",
296 "MembershipCredential",
297 "PersonhoodCredential"
298 ],
299 "issuer": "did:example:issuer",
300 "validFrom": "2025-12-11T00:00:00Z",
301 "credentialSubject": {
302 "id": "did:example:subject"
303 }
304}"#;
305
306 assert_eq!(txt, sample);
307 }
308 #[test]
309 fn test_vrc_serialization() {
310 let vrc = DTGCredential::new_vrc(
311 "did:example:issuer".to_string(),
312 "did:example:subject".to_string(),
313 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
314 .unwrap()
315 .with_timezone(&Utc),
316 None,
317 );
318
319 let txt = serde_json::to_string_pretty(&vrc).unwrap();
320 let sample = r#"{
321 "@context": [
322 "https://www.w3.org/ns/credentials/v2",
323 "https://firstperson.network/credentials/dtg/v1"
324 ],
325 "type": [
326 "VerifiableCredential",
327 "DTGCredential",
328 "RelationshipCredential"
329 ],
330 "issuer": "did:example:issuer",
331 "validFrom": "2025-12-11T00:00:00Z",
332 "credentialSubject": {
333 "id": "did:example:subject"
334 }
335}"#;
336
337 assert_eq!(txt, sample);
338 }
339
340 #[test]
341 fn test_vic_serialization() {
342 let vic = DTGCredential::new_vic(
343 "did:example:issuer".to_string(),
344 "did:example:subject".to_string(),
345 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
346 .unwrap()
347 .with_timezone(&Utc),
348 None,
349 );
350
351 let txt = serde_json::to_string_pretty(&vic).unwrap();
352 let sample = r#"{
353 "@context": [
354 "https://www.w3.org/ns/credentials/v2",
355 "https://firstperson.network/credentials/dtg/v1"
356 ],
357 "type": [
358 "VerifiableCredential",
359 "DTGCredential",
360 "InvitationCredential"
361 ],
362 "issuer": "did:example:issuer",
363 "validFrom": "2025-12-11T00:00:00Z",
364 "credentialSubject": {
365 "id": "did:example:subject"
366 }
367}"#;
368
369 assert_eq!(txt, sample);
370 }
371
372 #[test]
373 fn test_vpc_serialization() {
374 let vpc = DTGCredential::new_vpc(
375 "did:example:issuer".to_string(),
376 "did:example:subject".to_string(),
377 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
378 .unwrap()
379 .with_timezone(&Utc),
380 None,
381 );
382
383 let txt = serde_json::to_string_pretty(&vpc).unwrap();
384 let sample = r#"{
385 "@context": [
386 "https://www.w3.org/ns/credentials/v2",
387 "https://firstperson.network/credentials/dtg/v1"
388 ],
389 "type": [
390 "VerifiableCredential",
391 "DTGCredential",
392 "PersonaCredential"
393 ],
394 "issuer": "did:example:issuer",
395 "validFrom": "2025-12-11T00:00:00Z",
396 "credentialSubject": {
397 "id": "did:example:subject"
398 }
399}"#;
400
401 assert_eq!(txt, sample);
402 }
403
404 #[test]
405 fn test_vec_serialization() {
406 let vec = DTGCredential::new_vec(
407 "did:example:issuer".to_string(),
408 "did:example:subject".to_string(),
409 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
410 .unwrap()
411 .with_timezone(&Utc),
412 None,
413 json!({
414 "type": "SkillEndorsement",
415 "name": "Software Development",
416 "competencyLevel": "expert"
417 }),
418 );
419
420 let txt = serde_json::to_string_pretty(&vec).unwrap();
421 let sample = r#"{
422 "@context": [
423 "https://www.w3.org/ns/credentials/v2",
424 "https://firstperson.network/credentials/dtg/v1"
425 ],
426 "type": [
427 "VerifiableCredential",
428 "DTGCredential",
429 "EndorsementCredential"
430 ],
431 "issuer": "did:example:issuer",
432 "validFrom": "2025-12-11T00:00:00Z",
433 "credentialSubject": {
434 "id": "did:example:subject",
435 "endorsement": {
436 "competencyLevel": "expert",
437 "name": "Software Development",
438 "type": "SkillEndorsement"
439 }
440 }
441}"#;
442
443 assert_eq!(txt, sample);
444 }
445
446 #[test]
447 fn test_vwc_serialization() {
448 let vwc = DTGCredential::new_vwc(
449 "did:example:issuer".to_string(),
450 "did:example:subject".to_string(),
451 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
452 .unwrap()
453 .with_timezone(&Utc),
454 None,
455 Some("sha256:test1234".to_string()),
456 Some(WitnessContext {
457 event: Some("EthDenver 2024".to_string()),
458 session_id: Some("session-8822-nonce".to_string()),
459 method: Some("in-person-proximity".to_string()),
460 }),
461 );
462
463 let txt = serde_json::to_string_pretty(&vwc).unwrap();
464
465 let sample = r#"{
466 "@context": [
467 "https://www.w3.org/ns/credentials/v2",
468 "https://firstperson.network/credentials/dtg/v1"
469 ],
470 "type": [
471 "VerifiableCredential",
472 "DTGCredential",
473 "WitnessCredential"
474 ],
475 "issuer": "did:example:issuer",
476 "validFrom": "2025-12-11T00:00:00Z",
477 "credentialSubject": {
478 "id": "did:example:subject",
479 "digest": "sha256:test1234",
480 "witnessContext": {
481 "event": "EthDenver 2024",
482 "sessionId": "session-8822-nonce",
483 "method": "in-person-proximity"
484 }
485 }
486}"#;
487
488 assert_eq!(txt, sample);
489 }
490
491 #[test]
492 fn test_rcard_serialization() {
493 let rcard = DTGCredential::new_rcard(
494 "did:example:issuer".to_string(),
495 "did:example:subject".to_string(),
496 DateTime::parse_from_rfc3339("2025-12-11T00:00:00Z")
497 .unwrap()
498 .with_timezone(&Utc),
499 None,
500 json!([
501 "vcard",
502 [
503 ["fn", {}, "text", "Alice Smith"],
504 ["email", {}, "text", "alice@example.com"]
505 ]
506 ]),
507 );
508
509 let txt = serde_json::to_string_pretty(&rcard).unwrap();
510
511 let sample = r#"{
512 "@context": [
513 "https://www.w3.org/ns/credentials/v2",
514 "https://firstperson.network/credentials/dtg/v1"
515 ],
516 "type": [
517 "VerifiableCredential",
518 "DTGCredential",
519 "RCardCredential"
520 ],
521 "issuer": "did:example:issuer",
522 "validFrom": "2025-12-11T00:00:00Z",
523 "credentialSubject": {
524 "id": "did:example:subject",
525 "card": [
526 "vcard",
527 [
528 [
529 "fn",
530 {},
531 "text",
532 "Alice Smith"
533 ],
534 [
535 "email",
536 {},
537 "text",
538 "alice@example.com"
539 ]
540 ]
541 ]
542 }
543}"#;
544
545 assert_eq!(txt, sample);
546 }
547}