identity_credential/presentation/
presentation_builder.rs1#![allow(deprecated)]
5
6use identity_core::common::Context;
7use identity_core::common::Object;
8use identity_core::common::Url;
9use identity_core::common::Value;
10
11use crate::credential::Credential;
12use crate::credential::CredentialV2;
13use crate::credential::Policy;
14use crate::credential::RefreshService;
15use crate::error::Result;
16
17use super::Presentation;
18
19#[derive(Clone, Debug)]
21pub struct PresentationBuilder<CRED, T = Object> {
22 pub(crate) context: Vec<Context>,
23 pub(crate) id: Option<Url>,
24 pub(crate) types: Vec<String>,
25 pub(crate) credentials: Vec<CRED>,
26 pub(crate) holder: Url,
27 pub(crate) refresh_service: Vec<RefreshService>,
28 pub(crate) terms_of_use: Vec<Policy>,
29 pub(crate) properties: T,
30}
31
32impl<CRED, T> PresentationBuilder<CRED, T> {
33 pub fn new(holder: Url, properties: T) -> Self {
35 Self {
36 context: Vec::new(),
37 id: None,
38 types: vec![Presentation::<T>::base_type().into()],
39 credentials: Vec::new(),
40 holder,
41 refresh_service: Vec::new(),
42 terms_of_use: Vec::new(),
43 properties,
44 }
45 }
46
47 #[must_use]
49 pub fn context(mut self, value: impl Into<Context>) -> Self {
50 self.context.push(value.into());
51 self
52 }
53
54 #[must_use]
56 pub fn id(mut self, value: Url) -> Self {
57 self.id = Some(value);
58 self
59 }
60
61 #[must_use]
63 pub fn type_(mut self, value: impl Into<String>) -> Self {
64 self.types.push(value.into());
65 self
66 }
67
68 #[must_use]
70 pub fn credential(mut self, value: CRED) -> Self {
71 self.credentials.push(value);
72 self
73 }
74
75 #[must_use]
77 pub fn refresh_service(mut self, value: RefreshService) -> Self {
78 self.refresh_service.push(value);
79 self
80 }
81
82 #[must_use]
84 pub fn terms_of_use(mut self, value: Policy) -> Self {
85 self.terms_of_use.push(value);
86 self
87 }
88
89 pub fn build(mut self) -> Result<Presentation<CRED, T>> {
93 if self.context.first() != Some(Credential::<()>::base_context())
94 || self.context.first() != Some(CredentialV2::<()>::base_context())
95 {
96 self.context.insert(0, Credential::<()>::base_context().clone());
97 }
98
99 Presentation::from_builder(self)
100 }
101
102 pub fn build_v2(mut self) -> Result<Presentation<CRED, T>> {
106 if self.context.first() != Some(Credential::<()>::base_context())
107 || self.context.first() != Some(CredentialV2::<()>::base_context())
108 {
109 self.context.insert(0, CredentialV2::<()>::base_context().clone());
110 }
111
112 Presentation::from_builder(self)
113 }
114}
115
116impl PresentationBuilder<Object> {
117 #[must_use]
119 pub fn property<K, V>(mut self, key: K, value: V) -> Self
120 where
121 K: Into<String>,
122 V: Into<Value>,
123 {
124 self.properties.insert(key.into(), value.into());
125 self
126 }
127
128 #[must_use]
130 pub fn properties<K, V, I>(mut self, iter: I) -> Self
131 where
132 I: IntoIterator<Item = (K, V)>,
133 K: Into<String>,
134 V: Into<Value>,
135 {
136 self
137 .properties
138 .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
139 self
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use serde_json::json;
146 use serde_json::Value;
147
148 use identity_core::common::Object;
149 use identity_core::common::Url;
150 use identity_core::convert::FromJson;
151
152 use crate::credential::Credential;
153 use crate::credential::CredentialBuilder;
154 use crate::credential::Jwt;
155 use crate::credential::Subject;
156 use crate::presentation::Presentation;
157 use crate::presentation::PresentationBuilder;
158
159 fn subject() -> Subject {
160 let json: Value = json!({
161 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
162 "degree": {
163 "type": "BachelorDegree",
164 "name": "Bachelor of Science and Arts"
165 }
166 });
167
168 Subject::from_json_value(json).unwrap()
169 }
170
171 fn issuer() -> Url {
172 Url::parse("did:example:issuer").unwrap()
173 }
174
175 #[test]
176 fn test_presentation_builder_valid() {
177 let credential: Credential = CredentialBuilder::default()
178 .type_("ExampleCredential")
179 .subject(subject())
180 .issuer(issuer())
181 .build()
182 .unwrap();
183
184 let credential_jwt = Jwt::new(credential.serialize_jwt(None).unwrap());
185
186 let presentation: Presentation<Jwt> = PresentationBuilder::new(Url::parse("did:test:abc1").unwrap(), Object::new())
187 .type_("ExamplePresentation")
188 .credential(credential_jwt)
189 .build()
190 .unwrap();
191
192 assert_eq!(presentation.context.len(), 1);
193 assert_eq!(
194 presentation.context.get(0).unwrap(),
195 Presentation::<Object>::base_context()
196 );
197 assert_eq!(presentation.types.len(), 2);
198 assert_eq!(presentation.types.get(0).unwrap(), Presentation::<Object>::base_type());
199 assert_eq!(presentation.types.get(1).unwrap(), "ExamplePresentation");
200 assert_eq!(presentation.verifiable_credential.len(), 1);
201 }
202
203 #[test]
204 fn test_presentation_builder_valid_without_credentials() {
205 let presentation: Presentation<Jwt> = PresentationBuilder::new(Url::parse("did:test:abc1").unwrap(), Object::new())
206 .type_("ExamplePresentation")
207 .build()
208 .unwrap();
209
210 assert_eq!(presentation.context.len(), 1);
211 assert_eq!(
212 presentation.context.get(0).unwrap(),
213 Presentation::<Object>::base_context()
214 );
215 assert_eq!(presentation.types.len(), 2);
216 assert_eq!(presentation.types.get(0).unwrap(), Presentation::<Object>::base_type());
217 assert_eq!(presentation.types.get(1).unwrap(), "ExamplePresentation");
218 assert_eq!(presentation.verifiable_credential.len(), 0);
219 }
220}