Skip to main content

tap_msg/message/
tap_message_enum.rs

1//! Enum for all TAP message types
2//!
3//! This module provides an enum that encompasses all TAP message types
4//! and functionality to convert from PlainMessage to the appropriate TAP message.
5
6use 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/// Enum encompassing all TAP message types
17#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(untagged)]
19#[allow(clippy::large_enum_variant)]
20pub enum TapMessage {
21    /// Add agents message (TAIP-5)
22    AddAgents(AddAgents),
23    /// Authorize message (TAIP-8)
24    Authorize(Authorize),
25    /// Authorization required message (TAIP-2)
26    AuthorizationRequired(AuthorizationRequired),
27    /// Basic message (DIDComm 2.0)
28    BasicMessage(BasicMessage),
29    /// Cancel message (TAIP-11)
30    Cancel(Cancel),
31    /// Capture message (TAIP-17)
32    Capture(Capture),
33    /// Confirm relationship message (TAIP-14)
34    ConfirmRelationship(ConfirmRelationship),
35    /// Connect message (TAIP-2)
36    Connect(Connect),
37    /// DIDComm presentation message
38    DIDCommPresentation(DIDCommPresentation),
39    /// Error message
40    Error(ErrorBody),
41    /// Lock message (TAIP-17). Formerly known as Escrow; the `Escrow` type
42    /// alias still resolves to `Lock`.
43    Lock(Lock),
44    /// RFQ message (TAIP-18). Formerly known as Exchange; the `Exchange` type
45    /// alias still resolves to `Rfq`.
46    Rfq(Rfq),
47    /// Out of band message (TAIP-2)
48    OutOfBand(OutOfBand),
49    /// Payment message (TAIP-14)
50    Payment(Payment),
51    /// Quote message (TAIP-18)
52    Quote(Quote),
53    /// Presentation message (TAIP-6)
54    Presentation(Presentation),
55    /// Reject message (TAIP-10)
56    Reject(Reject),
57    /// Remove agent message (TAIP-5)
58    RemoveAgent(RemoveAgent),
59    /// Replace agent message (TAIP-5)
60    ReplaceAgent(ReplaceAgent),
61    /// Request presentation message (TAIP-6)
62    RequestPresentation(RequestPresentation),
63    /// Revert message (TAIP-12)
64    Revert(Revert),
65    /// Settle message (TAIP-9)
66    Settle(Settle),
67    /// Transfer message (TAIP-3)
68    Transfer(Transfer),
69    /// Trust Ping message (DIDComm 2.0)
70    TrustPing(TrustPing),
71    /// Trust Ping Response message (DIDComm 2.0)
72    TrustPingResponse(TrustPingResponse),
73    /// Update party message (TAIP-4)
74    UpdateParty(UpdateParty),
75    /// Update policies message (TAIP-7)
76    UpdatePolicies(UpdatePolicies),
77}
78
79impl TapMessage {
80    /// Convert a PlainMessage into the appropriate TapMessage variant
81    /// based on the message type field
82    pub fn from_plain_message(plain_msg: &PlainMessage) -> Result<Self> {
83        // Extract the type from either the type_ field or from the body's @type field
84        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        // Parse the message body based on the type
104        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            // Both the new `#Lock` URI and the legacy `#Escrow` URI dispatch to
182            // `TapMessage::Lock` — `Escrow` is a type alias for `Lock`.
183            "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            // Both the new `#RFQ` URI and the legacy `#Exchange` URI dispatch to
190            // `TapMessage::Rfq` — `Exchange` is a type alias for `Rfq`.
191            "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    /// Get the message type string for this TapMessage
311    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}