1use crate::didcomm::PlainMessage;
7use crate::error::{Error, Result};
8use crate::message::{
9 AddAgents, AuthorizationRequired, Authorize, BasicMessage, Cancel, Capture,
10 ConfirmRelationship, Connect, DIDCommPresentation, ErrorBody, Lock, OutOfBand, Payment,
11 Presentation, Quote, Reject, RemoveAgent, ReplaceAgent, RequestPresentation, Revert, Rfq,
12 Settle, Transfer, TrustPing, TrustPingResponse, UpdateParty, UpdatePolicies,
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(untagged)]
19#[allow(clippy::large_enum_variant)]
20pub enum TapMessage {
21 AddAgents(AddAgents),
23 Authorize(Authorize),
25 AuthorizationRequired(AuthorizationRequired),
27 BasicMessage(BasicMessage),
29 Cancel(Cancel),
31 Capture(Capture),
33 ConfirmRelationship(ConfirmRelationship),
35 Connect(Connect),
37 DIDCommPresentation(DIDCommPresentation),
39 Error(ErrorBody),
41 Lock(Lock),
44 Rfq(Rfq),
47 OutOfBand(OutOfBand),
49 Payment(Payment),
51 Quote(Quote),
53 Presentation(Presentation),
55 Reject(Reject),
57 RemoveAgent(RemoveAgent),
59 ReplaceAgent(ReplaceAgent),
61 RequestPresentation(RequestPresentation),
63 Revert(Revert),
65 Settle(Settle),
67 Transfer(Transfer),
69 TrustPing(TrustPing),
71 TrustPingResponse(TrustPingResponse),
73 UpdateParty(UpdateParty),
75 UpdatePolicies(UpdatePolicies),
77}
78
79impl TapMessage {
80 pub fn from_plain_message(plain_msg: &PlainMessage) -> Result<Self> {
83 let message_type =
85 if !plain_msg.type_.is_empty() && plain_msg.type_ != "application/didcomm-plain+json" {
86 &plain_msg.type_
87 } else if let Some(body_obj) = plain_msg.body.as_object() {
88 if let Some(type_val) = body_obj.get("@type") {
89 type_val.as_str().unwrap_or("")
90 } else {
91 ""
92 }
93 } else {
94 ""
95 };
96
97 if message_type.is_empty() {
98 return Err(Error::Validation(
99 "Message type not found in PlainMessage".to_string(),
100 ));
101 }
102
103 match message_type {
105 "https://tap.rsvp/schema/1.0#AddAgents" => {
106 let msg: AddAgents =
107 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
108 Error::SerializationError(format!("Failed to parse AddAgents: {}", e))
109 })?;
110 Ok(TapMessage::AddAgents(msg))
111 }
112 "https://tap.rsvp/schema/1.0#Authorize" => {
113 let msg: Authorize =
114 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
115 Error::SerializationError(format!("Failed to parse Authorize: {}", e))
116 })?;
117 Ok(TapMessage::Authorize(msg))
118 }
119 "https://tap.rsvp/schema/1.0#AuthorizationRequired" => {
120 let msg: AuthorizationRequired = serde_json::from_value(plain_msg.body.clone())
121 .map_err(|e| {
122 Error::SerializationError(format!(
123 "Failed to parse AuthorizationRequired: {}",
124 e
125 ))
126 })?;
127 Ok(TapMessage::AuthorizationRequired(msg))
128 }
129 "https://didcomm.org/basicmessage/2.0/message" => {
130 let msg: BasicMessage =
131 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
132 Error::SerializationError(format!("Failed to parse BasicMessage: {}", e))
133 })?;
134 Ok(TapMessage::BasicMessage(msg))
135 }
136 "https://tap.rsvp/schema/1.0#Cancel" => {
137 let msg: Cancel = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
138 Error::SerializationError(format!("Failed to parse Cancel: {}", e))
139 })?;
140 Ok(TapMessage::Cancel(msg))
141 }
142 "https://tap.rsvp/schema/1.0#Capture" => {
143 let msg: Capture = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
144 Error::SerializationError(format!("Failed to parse Capture: {}", e))
145 })?;
146 Ok(TapMessage::Capture(msg))
147 }
148 "https://tap.rsvp/schema/1.0#ConfirmRelationship" => {
149 let msg: ConfirmRelationship = serde_json::from_value(plain_msg.body.clone())
150 .map_err(|e| {
151 Error::SerializationError(format!(
152 "Failed to parse ConfirmRelationship: {}",
153 e
154 ))
155 })?;
156 Ok(TapMessage::ConfirmRelationship(msg))
157 }
158 "https://tap.rsvp/schema/1.0#Connect" => {
159 let msg: Connect = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
160 Error::SerializationError(format!("Failed to parse Connect: {}", e))
161 })?;
162 Ok(TapMessage::Connect(msg))
163 }
164 "https://didcomm.org/present-proof/3.0/presentation" => {
165 let msg: DIDCommPresentation = serde_json::from_value(plain_msg.body.clone())
166 .map_err(|e| {
167 Error::SerializationError(format!(
168 "Failed to parse DIDCommPresentation: {}",
169 e
170 ))
171 })?;
172 Ok(TapMessage::DIDCommPresentation(msg))
173 }
174 "https://tap.rsvp/schema/1.0#Error" => {
175 let msg: ErrorBody =
176 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
177 Error::SerializationError(format!("Failed to parse Error: {}", e))
178 })?;
179 Ok(TapMessage::Error(msg))
180 }
181 "https://tap.rsvp/schema/1.0#Lock" | "https://tap.rsvp/schema/1.0#Escrow" => {
184 let msg: Lock = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
185 Error::SerializationError(format!("Failed to parse Lock: {}", e))
186 })?;
187 Ok(TapMessage::Lock(msg))
188 }
189 "https://tap.rsvp/schema/1.0#RFQ" | "https://tap.rsvp/schema/1.0#Exchange" => {
192 let msg: Rfq = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
193 Error::SerializationError(format!("Failed to parse RFQ: {}", e))
194 })?;
195 Ok(TapMessage::Rfq(msg))
196 }
197 "https://tap.rsvp/schema/1.0#OutOfBand" => {
198 let msg: OutOfBand =
199 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
200 Error::SerializationError(format!("Failed to parse OutOfBand: {}", e))
201 })?;
202 Ok(TapMessage::OutOfBand(msg))
203 }
204 "https://tap.rsvp/schema/1.0#Payment" => {
205 let msg: Payment = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
206 Error::SerializationError(format!("Failed to parse Payment: {}", e))
207 })?;
208 Ok(TapMessage::Payment(msg))
209 }
210 "https://tap.rsvp/schema/1.0#Quote" => {
211 let msg: Quote = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
212 Error::SerializationError(format!("Failed to parse Quote: {}", e))
213 })?;
214 Ok(TapMessage::Quote(msg))
215 }
216 "https://tap.rsvp/schema/1.0#Presentation" => {
217 let msg: Presentation =
218 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
219 Error::SerializationError(format!("Failed to parse Presentation: {}", e))
220 })?;
221 Ok(TapMessage::Presentation(msg))
222 }
223 "https://tap.rsvp/schema/1.0#Reject" => {
224 let msg: Reject = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
225 Error::SerializationError(format!("Failed to parse Reject: {}", e))
226 })?;
227 Ok(TapMessage::Reject(msg))
228 }
229 "https://tap.rsvp/schema/1.0#RemoveAgent" => {
230 let msg: RemoveAgent =
231 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
232 Error::SerializationError(format!("Failed to parse RemoveAgent: {}", e))
233 })?;
234 Ok(TapMessage::RemoveAgent(msg))
235 }
236 "https://tap.rsvp/schema/1.0#ReplaceAgent" => {
237 let msg: ReplaceAgent =
238 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
239 Error::SerializationError(format!("Failed to parse ReplaceAgent: {}", e))
240 })?;
241 Ok(TapMessage::ReplaceAgent(msg))
242 }
243 "https://tap.rsvp/schema/1.0#RequestPresentation" => {
244 let msg: RequestPresentation = serde_json::from_value(plain_msg.body.clone())
245 .map_err(|e| {
246 Error::SerializationError(format!(
247 "Failed to parse RequestPresentation: {}",
248 e
249 ))
250 })?;
251 Ok(TapMessage::RequestPresentation(msg))
252 }
253 "https://tap.rsvp/schema/1.0#Revert" => {
254 let msg: Revert = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
255 Error::SerializationError(format!("Failed to parse Revert: {}", e))
256 })?;
257 Ok(TapMessage::Revert(msg))
258 }
259 "https://tap.rsvp/schema/1.0#Settle" => {
260 let msg: Settle = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
261 Error::SerializationError(format!("Failed to parse Settle: {}", e))
262 })?;
263 Ok(TapMessage::Settle(msg))
264 }
265 "https://tap.rsvp/schema/1.0#Transfer" => {
266 let msg: Transfer =
267 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
268 Error::SerializationError(format!("Failed to parse Transfer: {}", e))
269 })?;
270 Ok(TapMessage::Transfer(msg))
271 }
272 "https://tap.rsvp/schema/1.0#UpdateParty" => {
273 let msg: UpdateParty =
274 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
275 Error::SerializationError(format!("Failed to parse UpdateParty: {}", e))
276 })?;
277 Ok(TapMessage::UpdateParty(msg))
278 }
279 "https://tap.rsvp/schema/1.0#UpdatePolicies" => {
280 let msg: UpdatePolicies =
281 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
282 Error::SerializationError(format!("Failed to parse UpdatePolicies: {}", e))
283 })?;
284 Ok(TapMessage::UpdatePolicies(msg))
285 }
286 "https://didcomm.org/trust-ping/2.0/ping" => {
287 let msg: TrustPing =
288 serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
289 Error::SerializationError(format!("Failed to parse TrustPing: {}", e))
290 })?;
291 Ok(TapMessage::TrustPing(msg))
292 }
293 "https://didcomm.org/trust-ping/2.0/ping-response" => {
294 let msg: TrustPingResponse = serde_json::from_value(plain_msg.body.clone())
295 .map_err(|e| {
296 Error::SerializationError(format!(
297 "Failed to parse TrustPingResponse: {}",
298 e
299 ))
300 })?;
301 Ok(TapMessage::TrustPingResponse(msg))
302 }
303 _ => Err(Error::Validation(format!(
304 "Unknown message type: {}",
305 message_type
306 ))),
307 }
308 }
309
310 pub fn message_type(&self) -> &'static str {
312 match self {
313 TapMessage::AddAgents(_) => "https://tap.rsvp/schema/1.0#AddAgents",
314 TapMessage::Authorize(_) => "https://tap.rsvp/schema/1.0#Authorize",
315 TapMessage::AuthorizationRequired(_) => {
316 "https://tap.rsvp/schema/1.0#AuthorizationRequired"
317 }
318 TapMessage::BasicMessage(_) => "https://didcomm.org/basicmessage/2.0/message",
319 TapMessage::Cancel(_) => "https://tap.rsvp/schema/1.0#Cancel",
320 TapMessage::Capture(_) => "https://tap.rsvp/schema/1.0#Capture",
321 TapMessage::ConfirmRelationship(_) => "https://tap.rsvp/schema/1.0#ConfirmRelationship",
322 TapMessage::Connect(_) => "https://tap.rsvp/schema/1.0#Connect",
323 TapMessage::DIDCommPresentation(_) => {
324 "https://didcomm.org/present-proof/3.0/presentation"
325 }
326 TapMessage::Error(_) => "https://tap.rsvp/schema/1.0#Error",
327 TapMessage::Lock(_) => "https://tap.rsvp/schema/1.0#Lock",
328 TapMessage::Rfq(_) => "https://tap.rsvp/schema/1.0#RFQ",
329 TapMessage::OutOfBand(_) => "https://tap.rsvp/schema/1.0#OutOfBand",
330 TapMessage::Payment(_) => "https://tap.rsvp/schema/1.0#Payment",
331 TapMessage::Quote(_) => "https://tap.rsvp/schema/1.0#Quote",
332 TapMessage::Presentation(_) => "https://tap.rsvp/schema/1.0#Presentation",
333 TapMessage::Reject(_) => "https://tap.rsvp/schema/1.0#Reject",
334 TapMessage::RemoveAgent(_) => "https://tap.rsvp/schema/1.0#RemoveAgent",
335 TapMessage::ReplaceAgent(_) => "https://tap.rsvp/schema/1.0#ReplaceAgent",
336 TapMessage::RequestPresentation(_) => "https://tap.rsvp/schema/1.0#RequestPresentation",
337 TapMessage::Revert(_) => "https://tap.rsvp/schema/1.0#Revert",
338 TapMessage::Settle(_) => "https://tap.rsvp/schema/1.0#Settle",
339 TapMessage::Transfer(_) => "https://tap.rsvp/schema/1.0#Transfer",
340 TapMessage::TrustPing(_) => "https://didcomm.org/trust-ping/2.0/ping",
341 TapMessage::TrustPingResponse(_) => "https://didcomm.org/trust-ping/2.0/ping-response",
342 TapMessage::UpdateParty(_) => "https://tap.rsvp/schema/1.0#UpdateParty",
343 TapMessage::UpdatePolicies(_) => "https://tap.rsvp/schema/1.0#UpdatePolicies",
344 }
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 use serde_json::json;
352
353 #[test]
354 fn test_parse_transfer_body() {
355 let body = json!({
356 "@type": "https://tap.rsvp/schema/1.0#Transfer",
357 "transaction_id": "test-tx-123",
358 "asset": "eip155:1/slip44:60",
359 "originator": {
360 "@id": "did:example:alice"
361 },
362 "amount": "100",
363 "agents": [],
364 "metadata": {}
365 });
366
367 match serde_json::from_value::<Transfer>(body.clone()) {
368 Ok(transfer) => {
369 println!("Successfully parsed Transfer: {:?}", transfer);
370 assert_eq!(transfer.amount, "100");
371 }
372 Err(e) => {
373 panic!("Failed to parse Transfer: {}", e);
374 }
375 }
376 }
377
378 #[test]
379 fn test_from_plain_message_transfer() {
380 let plain_msg = PlainMessage {
381 id: "test-123".to_string(),
382 typ: "application/didcomm-plain+json".to_string(),
383 type_: "https://tap.rsvp/schema/1.0#Transfer".to_string(),
384 body: json!({
385 "@type": "https://tap.rsvp/schema/1.0#Transfer",
386 "transaction_id": "test-tx-456",
387 "asset": "eip155:1/slip44:60",
388 "originator": {
389 "@id": "did:example:alice"
390 },
391 "amount": "100",
392 "agents": [],
393 "metadata": {}
394 }),
395 from: "did:example:alice".to_string(),
396 to: vec!["did:example:bob".to_string()],
397 thid: None,
398 pthid: None,
399 created_time: Some(1234567890),
400 expires_time: None,
401 from_prior: None,
402 attachments: None,
403 extra_headers: Default::default(),
404 };
405
406 let tap_msg = TapMessage::from_plain_message(&plain_msg).unwrap();
407
408 match tap_msg {
409 TapMessage::Transfer(transfer) => {
410 assert_eq!(transfer.amount, "100");
411 assert_eq!(
412 transfer.originator.as_ref().unwrap().id,
413 "did:example:alice"
414 );
415 }
416 _ => panic!("Expected Transfer message"),
417 }
418 }
419
420 #[test]
421 fn test_message_type() {
422 let transfer = Transfer {
423 asset: "eip155:1/slip44:60".parse().unwrap(),
424 originator: Some(crate::message::Party::new("did:example:alice")),
425 beneficiary: None,
426 amount: "100".to_string(),
427 agents: vec![],
428 memo: None,
429 settlement_id: None,
430 expiry: None,
431 transaction_value: None,
432 connection_id: None,
433 transaction_id: Some("tx-123".to_string()),
434 metadata: Default::default(),
435 };
436
437 let tap_msg = TapMessage::Transfer(transfer);
438 assert_eq!(
439 tap_msg.message_type(),
440 "https://tap.rsvp/schema/1.0#Transfer"
441 );
442 }
443}