ssi_vc/v1/syntax/
presentation.rs

1use 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/// JSON Presentation.
30#[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    /// JSON-LD context.
37    #[serde(rename = "@context")]
38    pub context: Context,
39
40    /// Presentation identifier.
41    #[serde(
42        default,
43        deserialize_with = "not_null",
44        skip_serializing_if = "Option::is_none"
45    )]
46    pub id: Option<UriBuf>,
47
48    /// Presentation type.
49    #[serde(rename = "type")]
50    pub types: JsonPresentationTypes,
51
52    /// Holder.
53    #[serde(rename = "holder")]
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub holder: Option<UriBuf>,
56
57    /// Verifiable credentials.
58    #[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    /// Verifiable credential type.
116    type Credential = C;
117
118    /// Identifier.
119    fn id(&self) -> Option<&Uri> {
120        self.id.as_deref()
121    }
122
123    /// Types, without the `VerifiablePresentation` type.
124    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}