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