1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
8pub struct PlainMessage {
9 pub id: String,
11
12 #[serde(default = "default_typ")]
14 pub typ: String,
15
16 #[serde(rename = "type")]
21 pub type_: String,
22
23 pub body: Value,
25
26 pub from: String,
29
30 pub to: Vec<String>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
38 pub thid: Option<String>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
43 pub pthid: Option<String>,
44
45 #[serde(flatten)]
47 #[serde(skip_serializing_if = "HashMap::is_empty")]
48 pub extra_headers: HashMap<String, Value>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
55 pub created_time: Option<u64>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
62 pub expires_time: Option<u64>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub from_prior: Option<String>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub attachments: Option<Vec<Attachment>>,
70}
71
72const PLAINTEXT_TYP: &str = "application/didcomm-plain+json";
73
74fn default_typ() -> String {
75 PLAINTEXT_TYP.to_string()
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct OutOfBand {
81 #[serde(rename = "goal_code")]
83 pub goal_code: String,
84
85 pub id: String,
87
88 pub label: String,
90
91 pub accept: Option<String>,
93
94 pub services: Vec<serde_json::Value>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
104pub struct SimpleAttachmentData {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub base64: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub json: Option<serde_json::Value>,
112}
113
114#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
116pub struct Attachment {
117 pub data: AttachmentData,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
128 pub id: Option<String>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub description: Option<String>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
138 pub filename: Option<String>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub media_type: Option<String>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub format: Option<String>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
151 pub lastmod_time: Option<u64>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
157 pub byte_count: Option<u64>,
158}
159
160impl Attachment {
161 pub fn base64(base64: String) -> AttachmentBuilder {
162 AttachmentBuilder::new(AttachmentData::Base64 {
163 value: Base64AttachmentData { base64, jws: None },
164 })
165 }
166
167 pub fn json(json: Value) -> AttachmentBuilder {
168 AttachmentBuilder::new(AttachmentData::Json {
169 value: JsonAttachmentData { json, jws: None },
170 })
171 }
172
173 pub fn links(links: Vec<String>, hash: String) -> AttachmentBuilder {
174 AttachmentBuilder::new(AttachmentData::Links {
175 value: LinksAttachmentData {
176 links,
177 hash,
178 jws: None,
179 },
180 })
181 }
182}
183
184pub struct AttachmentBuilder {
185 data: AttachmentData,
186 id: Option<String>,
187 description: Option<String>,
188 filename: Option<String>,
189 media_type: Option<String>,
190 format: Option<String>,
191 lastmod_time: Option<u64>,
192 byte_count: Option<u64>,
193}
194
195impl AttachmentBuilder {
196 fn new(data: AttachmentData) -> Self {
197 AttachmentBuilder {
198 data,
199 id: None,
200 description: None,
201 filename: None,
202 media_type: None,
203 format: None,
204 lastmod_time: None,
205 byte_count: None,
206 }
207 }
208
209 pub fn id(mut self, id: String) -> Self {
210 self.id = Some(id);
211 self
212 }
213
214 pub fn description(mut self, description: String) -> Self {
215 self.description = Some(description);
216 self
217 }
218
219 pub fn filename(mut self, filename: String) -> Self {
220 self.filename = Some(filename);
221 self
222 }
223
224 pub fn media_type(mut self, media_type: String) -> Self {
225 self.media_type = Some(media_type);
226 self
227 }
228
229 pub fn format(mut self, format: String) -> Self {
230 self.format = Some(format);
231 self
232 }
233
234 pub fn lastmod_time(mut self, lastmod_time: u64) -> Self {
235 self.lastmod_time = Some(lastmod_time);
236 self
237 }
238
239 pub fn byte_count(mut self, byte_count: u64) -> Self {
240 self.byte_count = Some(byte_count);
241 self
242 }
243
244 pub fn jws(mut self, jws: String) -> Self {
245 match self.data {
246 AttachmentData::Base64 { ref mut value } => value.jws = Some(jws),
247 AttachmentData::Json { ref mut value } => value.jws = Some(jws),
248 AttachmentData::Links { ref mut value } => value.jws = Some(jws),
249 }
250
251 self
252 }
253
254 pub fn finalize(self) -> Attachment {
255 Attachment {
256 data: self.data,
257 id: self.id,
258 description: self.description,
259 filename: self.filename,
260 media_type: self.media_type,
261 format: self.format,
262 lastmod_time: self.lastmod_time,
263 byte_count: self.byte_count,
264 }
265 }
266}
267
268#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
275#[serde(untagged)]
276pub enum AttachmentData {
277 Base64 {
278 #[serde(flatten)]
279 value: Base64AttachmentData,
280 },
281 Json {
282 #[serde(flatten)]
283 value: JsonAttachmentData,
284 },
285 Links {
286 #[serde(flatten)]
287 value: LinksAttachmentData,
288 },
289}
290
291#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
292pub struct Base64AttachmentData {
293 pub base64: String,
295
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub jws: Option<String>,
299}
300
301#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
302pub struct JsonAttachmentData {
303 pub json: Value,
305
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub jws: Option<String>,
309}
310
311#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
312pub struct LinksAttachmentData {
313 pub links: Vec<String>,
315
316 pub hash: String,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
321 pub jws: Option<String>,
322}
323
324#[cfg(test)]
325mod tests {
326 use core::panic;
327 use serde_json::json;
328
329 use super::*;
330
331 #[test]
332 fn attachment_base64_works() {
333 let attachment = Attachment::base64("ZXhhbXBsZQ==".to_owned())
334 .id("example-1".to_owned())
335 .description("example-1-description".to_owned())
336 .filename("attachment-1".to_owned())
337 .media_type("message/example".to_owned())
338 .format("json".to_owned())
339 .lastmod_time(10000)
340 .byte_count(200)
341 .jws("jws".to_owned())
342 .finalize();
343
344 let data = match attachment.data {
345 AttachmentData::Base64 { ref value } => value,
346 _ => panic!("data isn't base64."),
347 };
348
349 assert_eq!(data.base64, "ZXhhbXBsZQ==");
350 assert_eq!(data.jws, Some("jws".to_owned()));
351 assert_eq!(attachment.id, Some("example-1".to_owned()));
352
353 assert_eq!(
354 attachment.description,
355 Some("example-1-description".to_owned())
356 );
357
358 assert_eq!(attachment.filename, Some("attachment-1".to_owned()));
359 assert_eq!(attachment.media_type, Some("message/example".to_owned()));
360 assert_eq!(attachment.format, Some("json".to_owned()));
361 assert_eq!(attachment.lastmod_time, Some(10000));
362 assert_eq!(attachment.byte_count, Some(200));
363 }
364
365 #[test]
366 fn attachment_json_works() {
367 let attachment = Attachment::json(json!("example"))
368 .id("example-1".to_owned())
369 .description("example-1-description".to_owned())
370 .filename("attachment-1".to_owned())
371 .media_type("message/example".to_owned())
372 .format("json".to_owned())
373 .lastmod_time(10000)
374 .byte_count(200)
375 .jws("jws".to_owned())
376 .finalize();
377
378 let data = match attachment.data {
379 AttachmentData::Json { ref value } => value,
380 _ => panic!("data isn't json."),
381 };
382
383 assert_eq!(data.json, json!("example"));
384 assert_eq!(data.jws, Some("jws".to_owned()));
385 assert_eq!(attachment.id, Some("example-1".to_owned()));
386
387 assert_eq!(
388 attachment.description,
389 Some("example-1-description".to_owned())
390 );
391
392 assert_eq!(attachment.filename, Some("attachment-1".to_owned()));
393 assert_eq!(attachment.media_type, Some("message/example".to_owned()));
394 assert_eq!(attachment.format, Some("json".to_owned()));
395 assert_eq!(attachment.lastmod_time, Some(10000));
396 assert_eq!(attachment.byte_count, Some(200));
397 }
398
399 #[test]
400 fn attachment_links_works() {
401 let attachment = Attachment::links(
402 vec!["http://example1".to_owned(), "https://example2".to_owned()],
403 "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c".to_owned(),
404 )
405 .id("example-1".to_owned())
406 .description("example-1-description".to_owned())
407 .filename("attachment-1".to_owned())
408 .media_type("message/example".to_owned())
409 .format("json".to_owned())
410 .lastmod_time(10000)
411 .byte_count(200)
412 .jws("jws".to_owned())
413 .finalize();
414
415 let data = match attachment.data {
416 AttachmentData::Links { ref value } => value,
417 _ => panic!("data isn't links."),
418 };
419
420 assert_eq!(
421 data.links,
422 vec!["http://example1".to_owned(), "https://example2".to_owned()]
423 );
424
425 assert_eq!(
426 data.hash,
427 "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c".to_owned()
428 );
429
430 assert_eq!(data.jws, Some("jws".to_owned()));
431 assert_eq!(attachment.id, Some("example-1".to_owned()));
432
433 assert_eq!(
434 attachment.description,
435 Some("example-1-description".to_owned())
436 );
437
438 assert_eq!(attachment.filename, Some("attachment-1".to_owned()));
439 assert_eq!(attachment.media_type, Some("message/example".to_owned()));
440 assert_eq!(attachment.format, Some("json".to_owned()));
441 assert_eq!(attachment.lastmod_time, Some(10000));
442 assert_eq!(attachment.byte_count, Some(200));
443 }
444}