tap_msg/message/
did_presentation.rs1use serde::{Deserialize, Serialize};
6
7use crate::didcomm::Attachment;
8use crate::didcomm::PlainMessage;
9use crate::error::{Error, Result};
10use crate::message::tap_message_trait::TapMessageBody;
11use chrono::Utc;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct DIDCommPresentation {
16 pub formats: Vec<String>,
18
19 pub attachments: Vec<Attachment>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub thid: Option<String>,
25}
26
27impl TapMessageBody for DIDCommPresentation {
28 fn message_type() -> &'static str {
29 "https://didcomm.org/present-proof/3.0/presentation"
30 }
31
32 fn validate(&self) -> Result<()> {
33 if self.attachments.is_empty() {
35 return Err(Error::Validation(
36 "Presentation must have at least one attachment".to_string(),
37 ));
38 }
39
40 for (i, attachment) in self.attachments.iter().enumerate() {
42 if let Some(id) = &attachment.id {
43 if id.is_empty() {
44 return Err(Error::Validation(format!(
45 "Attachment {} has an empty ID",
46 i
47 )));
48 }
49 }
50 }
51
52 if self.formats.is_empty() {
54 return Err(Error::Validation(
55 "Presentation must have at least one format specified".to_string(),
56 ));
57 }
58
59 for (i, attachment) in self.attachments.iter().enumerate() {
61 if attachment.format.is_none() {
62 return Err(Error::Validation(format!(
63 "Attachment {} is missing the 'format' field",
64 i
65 )));
66 }
67 }
68
69 Ok(())
70 }
71
72 fn to_didcomm(&self, from: &str) -> Result<PlainMessage> {
73 let body_json =
75 serde_json::to_value(self).map_err(|e| Error::SerializationError(e.to_string()))?;
76
77 let now = Utc::now().timestamp() as u64;
78
79 let message = PlainMessage {
81 id: uuid::Uuid::new_v4().to_string(),
82 typ: "application/didcomm-plain+json".to_string(),
83 type_: Self::message_type().to_string(),
84 body: body_json,
85 from: from.to_string(),
86 to: Vec::new(), thid: self.thid.clone(),
88 pthid: None,
89 created_time: Some(now),
90 expires_time: None,
91 extra_headers: std::collections::HashMap::new(),
92 from_prior: None,
93 attachments: None,
94 };
95
96 Ok(message)
97 }
98
99 fn from_didcomm(message: &PlainMessage) -> Result<Self> {
100 if message.type_ != Self::message_type() {
102 return Err(Error::InvalidMessageType(format!(
103 "Expected {} but got {}",
104 Self::message_type(),
105 message.type_
106 )));
107 }
108
109 let body = message.body.clone();
111 let mut body_obj = body
112 .as_object()
113 .ok_or_else(|| Error::SerializationError("Body is not a JSON object".to_string()))?
114 .clone();
115
116 let mut attachments_in_body = if body_obj.contains_key("attachments") {
118 match &body_obj["attachments"] {
119 serde_json::Value::Array(arr) => arr.clone(),
120 _ => Vec::new(),
121 }
122 } else {
123 Vec::new()
124 };
125
126 if let Some(msg_attachments) = &message.attachments {
128 if let Ok(serde_json::Value::Array(arr)) = serde_json::to_value(msg_attachments) {
130 attachments_in_body.extend(arr);
131 }
132 }
133
134 body_obj.insert(
136 "attachments".to_string(),
137 serde_json::Value::Array(attachments_in_body.clone()),
138 );
139
140 if !body_obj.contains_key("formats") {
142 let mut formats = Vec::new();
144 for attachment in &attachments_in_body {
145 if let Some(format) = attachment.get("format") {
146 if let Some(format_str) = format.as_str() {
147 formats.push(format_str.to_string());
148 }
149 }
150 }
151
152 if formats.is_empty() {
154 formats.push("dif/presentation-exchange/submission@v1.0".to_string());
155 }
156
157 body_obj.insert(
158 "formats".to_string(),
159 serde_json::to_value(formats).unwrap(),
160 );
161 }
162
163 let mut presentation: DIDCommPresentation =
165 serde_json::from_value(serde_json::Value::Object(body_obj))
166 .map_err(|e| Error::SerializationError(e.to_string()))?;
167
168 if presentation.thid.is_none() {
170 presentation.thid = message.thid.clone();
171 }
172
173 Ok(presentation)
174 }
175}
176
177impl DIDCommPresentation {
178 pub fn new(formats: Vec<String>, attachments: Vec<Attachment>, thid: Option<String>) -> Self {
180 Self {
181 formats,
182 attachments,
183 thid,
184 }
185 }
186
187 }