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