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