ssi_vc/v1/syntax/
presentation.rs1use std::{borrow::Cow, collections::BTreeMap, hash::Hash};
2
3use crate::syntax::{not_null, value_or_array, RequiredType, TypeSerializationPolicy, Types};
4use crate::v1::{Context, Credential};
5use iref::{Uri, UriBuf};
6use linked_data::{LinkedDataResource, LinkedDataSubject};
7use rdf_types::VocabularyMut;
8use serde::{Deserialize, Serialize};
9use ssi_claims_core::{ClaimsValidity, ValidateClaims};
10use ssi_json_ld::{JsonLdError, JsonLdNodeObject, JsonLdObject, JsonLdTypes, Loader};
11use ssi_rdf::{Interpretation, LdEnvironment};
12
13use super::SpecializedJsonCredential;
14
15pub const VERIFIABLE_PRESENTATION_TYPE: &str = "VerifiablePresentation";
16
17pub struct PresentationType;
18
19impl RequiredType for PresentationType {
20 const REQUIRED_TYPE: &'static str = VERIFIABLE_PRESENTATION_TYPE;
21}
22
23impl TypeSerializationPolicy for PresentationType {
24 const PREFER_ARRAY: bool = true;
25}
26
27pub type JsonPresentationTypes<T = ()> = Types<PresentationType, T>;
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(bound(
32 serialize = "C: serde::Serialize",
33 deserialize = "C: serde::Deserialize<'de>"
34))]
35pub struct JsonPresentation<C = SpecializedJsonCredential> {
36 #[serde(rename = "@context")]
38 pub context: Context,
39
40 #[serde(
42 default,
43 deserialize_with = "not_null",
44 skip_serializing_if = "Option::is_none"
45 )]
46 pub id: Option<UriBuf>,
47
48 #[serde(rename = "type")]
50 pub types: JsonPresentationTypes,
51
52 #[serde(rename = "holder")]
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub holder: Option<UriBuf>,
56
57 #[serde(rename = "verifiableCredential")]
59 #[serde(
60 with = "value_or_array",
61 default,
62 skip_serializing_if = "Vec::is_empty"
63 )]
64 pub verifiable_credentials: Vec<C>,
65
66 #[serde(flatten)]
67 pub additional_properties: BTreeMap<String, json_syntax::Value>,
68}
69
70impl Default for JsonPresentation {
71 fn default() -> Self {
72 Self {
73 context: Context::default(),
74 id: None,
75 types: JsonPresentationTypes::default(),
76 verifiable_credentials: Vec::new(),
77 holder: None,
78 additional_properties: BTreeMap::new(),
79 }
80 }
81}
82
83impl<C> JsonPresentation<C> {
84 pub fn new(id: Option<UriBuf>, holder: Option<UriBuf>, verifiable_credentials: Vec<C>) -> Self {
85 Self {
86 context: Context::default(),
87 id,
88 types: JsonPresentationTypes::default(),
89 holder,
90 verifiable_credentials,
91 additional_properties: BTreeMap::new(),
92 }
93 }
94}
95
96impl<C> JsonLdObject for JsonPresentation<C> {
97 fn json_ld_context(&self) -> Option<Cow<ssi_json_ld::syntax::Context>> {
98 Some(Cow::Borrowed(self.context.as_ref()))
99 }
100}
101
102impl<C> JsonLdNodeObject for JsonPresentation<C> {
103 fn json_ld_type(&self) -> JsonLdTypes {
104 self.types.to_json_ld_types()
105 }
106}
107
108impl<C, E, P> ValidateClaims<E, P> for JsonPresentation<C> {
109 fn validate_claims(&self, _: &E, _: &P) -> ClaimsValidity {
110 Ok(())
111 }
112}
113
114impl<C: Credential> crate::v1::Presentation for JsonPresentation<C> {
115 type Credential = C;
117
118 fn id(&self) -> Option<&Uri> {
120 self.id.as_deref()
121 }
122
123 fn additional_types(&self) -> &[String] {
125 self.types.additional_types()
126 }
127
128 fn verifiable_credentials(&self) -> &[Self::Credential] {
129 &self.verifiable_credentials
130 }
131
132 fn holder(&self) -> Option<&Uri> {
133 self.holder.as_deref()
134 }
135}
136
137impl<C> ssi_json_ld::Expandable for JsonPresentation<C>
138where
139 C: Serialize,
140{
141 type Error = JsonLdError;
142
143 type Expanded<I, V>
144 = ssi_json_ld::ExpandedDocument<V::Iri, V::BlankId>
145 where
146 I: Interpretation,
147 V: VocabularyMut,
148 V::Iri: LinkedDataResource<I, V> + LinkedDataSubject<I, V>,
149 V::BlankId: LinkedDataResource<I, V> + LinkedDataSubject<I, V>;
150
151 async fn expand_with<I, V>(
152 &self,
153 ld: &mut LdEnvironment<V, I>,
154 loader: &impl Loader,
155 ) -> Result<Self::Expanded<I, V>, Self::Error>
156 where
157 I: Interpretation,
158 V: VocabularyMut,
159 V::Iri: Clone + Eq + Hash + LinkedDataResource<I, V> + LinkedDataSubject<I, V>,
160 V::BlankId: Clone + Eq + Hash + LinkedDataResource<I, V> + LinkedDataSubject<I, V>,
161 {
162 let json = ssi_json_ld::CompactJsonLd(json_syntax::to_value(self).unwrap());
163 json.expand_with(ld, loader).await
164 }
165}