identity_credential/credential/
builder.rs1use identity_core::common::Context;
5use identity_core::common::Object;
6use identity_core::common::Timestamp;
7use identity_core::common::Url;
8use identity_core::common::Value;
9
10use crate::credential::Credential;
11use crate::credential::CredentialV2;
12use crate::credential::Evidence;
13use crate::credential::Issuer;
14use crate::credential::Policy;
15use crate::credential::RefreshService;
16use crate::credential::Schema;
17use crate::credential::Status;
18use crate::credential::Subject;
19use crate::error::Result;
20
21use super::Proof;
22
23#[derive(Clone, Debug, Default)]
25pub struct CredentialBuilder<T = Object> {
26 pub(crate) context: Vec<Context>,
27 pub(crate) id: Option<Url>,
28 pub(crate) types: Vec<String>,
29 pub(crate) subject: Vec<Subject>,
30 pub(crate) issuer: Option<Issuer>,
31 pub(crate) issuance_date: Option<Timestamp>,
32 pub(crate) expiration_date: Option<Timestamp>,
33 pub(crate) status: Option<Status>,
34 pub(crate) schema: Vec<Schema>,
35 pub(crate) refresh_service: Vec<RefreshService>,
36 pub(crate) terms_of_use: Vec<Policy>,
37 pub(crate) evidence: Vec<Evidence>,
38 pub(crate) non_transferable: Option<bool>,
39 pub(crate) proof: Option<Proof>,
40 pub(crate) properties: T,
41}
42
43impl<T> CredentialBuilder<T> {
44 pub fn new(properties: T) -> Self {
46 Self {
47 context: Vec::new(),
48 id: None,
49 types: Vec::new(),
50 subject: Vec::new(),
51 issuer: None,
52 issuance_date: None,
53 expiration_date: None,
54 status: None,
55 schema: Vec::new(),
56 refresh_service: Vec::new(),
57 terms_of_use: Vec::new(),
58 evidence: Vec::new(),
59 non_transferable: None,
60 proof: None,
61 properties,
62 }
63 }
64
65 #[must_use]
67 pub fn context(mut self, value: impl Into<Context>) -> Self {
68 self.context.push(value.into());
69 self
70 }
71
72 #[must_use]
74 pub fn id(mut self, value: Url) -> Self {
75 self.id = Some(value);
76 self
77 }
78
79 #[must_use]
81 pub fn type_(mut self, value: impl Into<String>) -> Self {
82 self.types.push(value.into());
83 self
84 }
85
86 #[must_use]
88 pub fn subject(mut self, value: Subject) -> Self {
89 self.subject.push(value);
90 self
91 }
92
93 #[must_use]
95 pub fn subjects<I: IntoIterator<Item = Subject>>(mut self, values: I) -> Self {
96 for value in values {
97 self.subject.push(value);
98 }
99 self
100 }
101
102 #[must_use]
104 pub fn issuer(mut self, value: impl Into<Issuer>) -> Self {
105 self.issuer = Some(value.into());
106 self
107 }
108
109 #[must_use]
111 pub fn issuance_date(mut self, value: Timestamp) -> Self {
112 self.issuance_date = Some(value);
113 self
114 }
115
116 #[must_use]
118 pub fn expiration_date(mut self, value: Timestamp) -> Self {
119 self.expiration_date = Some(value);
120 self
121 }
122
123 #[must_use]
125 pub fn valid_from(mut self, value: Timestamp) -> Self {
126 self.issuance_date = Some(value);
127 self
128 }
129
130 #[must_use]
132 pub fn valid_until(mut self, value: Timestamp) -> Self {
133 self.expiration_date = Some(value);
134 self
135 }
136
137 #[must_use]
139 pub fn status(mut self, value: impl Into<Status>) -> Self {
140 self.status = Some(value.into());
141 self
142 }
143
144 #[must_use]
146 pub fn schema(mut self, value: Schema) -> Self {
147 self.schema.push(value);
148 self
149 }
150
151 #[must_use]
153 pub fn refresh_service(mut self, value: RefreshService) -> Self {
154 self.refresh_service.push(value);
155 self
156 }
157
158 #[must_use]
160 pub fn terms_of_use(mut self, value: Policy) -> Self {
161 self.terms_of_use.push(value);
162 self
163 }
164
165 #[must_use]
167 pub fn evidence(mut self, value: Evidence) -> Self {
168 self.evidence.push(value);
169 self
170 }
171
172 #[must_use]
174 pub fn non_transferable(mut self, value: bool) -> Self {
175 self.non_transferable = Some(value);
176 self
177 }
178
179 #[must_use]
181 pub fn proof(mut self, value: Proof) -> Self {
182 self.proof = Some(value);
183 self
184 }
185
186 pub fn build(self) -> Result<Credential<T>> {
188 Credential::from_builder(self)
189 }
190
191 pub fn build_v2(self) -> Result<CredentialV2<T>> {
193 CredentialV2::from_builder(self)
194 }
195}
196
197impl CredentialBuilder {
198 #[must_use]
200 pub fn property<K, V>(mut self, key: K, value: V) -> Self
201 where
202 K: Into<String>,
203 V: Into<Value>,
204 {
205 self.properties.insert(key.into(), value.into());
206 self
207 }
208
209 #[must_use]
211 pub fn properties<K, V, I>(mut self, iter: I) -> Self
212 where
213 I: IntoIterator<Item = (K, V)>,
214 K: Into<String>,
215 V: Into<Value>,
216 {
217 self
218 .properties
219 .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
220 self
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use identity_core::common::Object;
227 use identity_core::common::Timestamp;
228 use identity_core::common::Url;
229 use identity_core::convert::FromJson;
230 use serde_json::json;
231 use serde_json::Value;
232
233 use crate::credential::Credential;
234 use crate::credential::CredentialBuilder;
235 use crate::credential::Proof;
236 use crate::credential::Subject;
237
238 fn subject() -> Subject {
239 let json: Value = json!({
240 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
241 "degree": {
242 "type": "BachelorDegree",
243 "name": "Bachelor of Science and Arts"
244 }
245 });
246
247 Subject::from_json_value(json).unwrap()
248 }
249
250 fn issuer() -> Url {
251 Url::parse("did:example:issuer").unwrap()
252 }
253
254 #[test]
255 fn test_credential_builder_valid() {
256 let proof = Proof::new("test-type".to_owned(), Object::new());
257 let credential: Credential = CredentialBuilder::default()
258 .context(Url::parse("https://www.w3.org/2018/credentials/examples/v1").unwrap())
259 .id(Url::parse("http://example.edu/credentials/3732").unwrap())
260 .type_("UniversityDegreeCredential")
261 .subject(subject())
262 .issuer(issuer())
263 .issuance_date(Timestamp::parse("2010-01-01T00:00:00Z").unwrap())
264 .proof(proof)
265 .build()
266 .unwrap();
267
268 assert_eq!(credential.context.len(), 2);
269 assert_eq!(credential.context.get(0).unwrap(), Credential::<Object>::base_context());
270 assert_eq!(
271 credential.context.get(1).unwrap(),
272 "https://www.w3.org/2018/credentials/examples/v1"
273 );
274 assert_eq!(credential.id.unwrap(), "http://example.edu/credentials/3732");
275 assert_eq!(credential.types.len(), 2);
276 assert_eq!(credential.types.get(0).unwrap(), Credential::<Object>::base_type());
277 assert_eq!(credential.types.get(1).unwrap(), "UniversityDegreeCredential");
278 assert_eq!(credential.credential_subject.len(), 1);
279 assert_eq!(credential.issuer.url(), "did:example:issuer");
280 assert_eq!(credential.issuance_date.to_string(), "2010-01-01T00:00:00Z");
281 assert_eq!(
282 credential.credential_subject.get(0).unwrap().id.as_ref().unwrap(),
283 "did:example:ebfeb1f712ebc6f1c276e12ec21"
284 );
285 assert_eq!(
286 credential.credential_subject.get(0).unwrap().properties["degree"]["type"],
287 "BachelorDegree"
288 );
289 assert_eq!(
290 credential.credential_subject.get(0).unwrap().properties["degree"]["name"],
291 "Bachelor of Science and Arts"
292 );
293 assert_eq!(credential.proof.unwrap().type_, "test-type");
294 }
295
296 #[test]
297 #[should_panic = "MissingSubject"]
298 fn test_builder_missing_subjects() {
299 let _: Credential = CredentialBuilder::default().issuer(issuer()).build().unwrap();
300 }
301
302 #[test]
303 #[should_panic = "MissingIssuer"]
304 fn test_builder_missing_issuer() {
305 let _: Credential = CredentialBuilder::default().subject(subject()).build().unwrap();
306 }
307}