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, ConfirmRelationship,
10    Connect, DIDCommPresentation, ErrorBody, OutOfBand, Payment, Presentation, Reject, RemoveAgent,
11    ReplaceAgent, RequestPresentation, Revert, Settle, Transfer, TrustPing, TrustPingResponse,
12    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    /// Confirm relationship message (TAIP-14)
32    ConfirmRelationship(ConfirmRelationship),
33    /// Connect message (TAIP-2)
34    Connect(Connect),
35    /// DIDComm presentation message
36    DIDCommPresentation(DIDCommPresentation),
37    /// Error message
38    Error(ErrorBody),
39    /// Out of band message (TAIP-2)
40    OutOfBand(OutOfBand),
41    /// Payment message (TAIP-13)
42    Payment(Payment),
43    /// Presentation message (TAIP-6)
44    Presentation(Presentation),
45    /// Reject message (TAIP-10)
46    Reject(Reject),
47    /// Remove agent message (TAIP-5)
48    RemoveAgent(RemoveAgent),
49    /// Replace agent message (TAIP-5)
50    ReplaceAgent(ReplaceAgent),
51    /// Request presentation message (TAIP-6)
52    RequestPresentation(RequestPresentation),
53    /// Revert message (TAIP-12)
54    Revert(Revert),
55    /// Settle message (TAIP-9)
56    Settle(Settle),
57    /// Transfer message (TAIP-3)
58    Transfer(Transfer),
59    /// Trust Ping message (DIDComm 2.0)
60    TrustPing(TrustPing),
61    /// Trust Ping Response message (DIDComm 2.0)
62    TrustPingResponse(TrustPingResponse),
63    /// Update party message (TAIP-4)
64    UpdateParty(UpdateParty),
65    /// Update policies message (TAIP-7)
66    UpdatePolicies(UpdatePolicies),
67}
68
69impl TapMessage {
70    /// Convert a PlainMessage into the appropriate TapMessage variant
71    /// based on the message type field
72    pub fn from_plain_message(plain_msg: &PlainMessage) -> Result<Self> {
73        // Extract the type from either the type_ field or from the body's @type field
74        let message_type =
75            if !plain_msg.type_.is_empty() && plain_msg.type_ != "application/didcomm-plain+json" {
76                &plain_msg.type_
77            } else if let Some(body_obj) = plain_msg.body.as_object() {
78                if let Some(type_val) = body_obj.get("@type") {
79                    type_val.as_str().unwrap_or("")
80                } else {
81                    ""
82                }
83            } else {
84                ""
85            };
86
87        if message_type.is_empty() {
88            return Err(Error::Validation(
89                "Message type not found in PlainMessage".to_string(),
90            ));
91        }
92
93        // Parse the message body based on the type
94        match message_type {
95            "https://tap.rsvp/schema/1.0#AddAgents" => {
96                let msg: AddAgents =
97                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
98                        Error::SerializationError(format!("Failed to parse AddAgents: {}", e))
99                    })?;
100                Ok(TapMessage::AddAgents(msg))
101            }
102            "https://tap.rsvp/schema/1.0#Authorize" => {
103                let msg: Authorize =
104                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
105                        Error::SerializationError(format!("Failed to parse Authorize: {}", e))
106                    })?;
107                Ok(TapMessage::Authorize(msg))
108            }
109            "https://tap.rsvp/schema/1.0#AuthorizationRequired" => {
110                let msg: AuthorizationRequired = serde_json::from_value(plain_msg.body.clone())
111                    .map_err(|e| {
112                        Error::SerializationError(format!(
113                            "Failed to parse AuthorizationRequired: {}",
114                            e
115                        ))
116                    })?;
117                Ok(TapMessage::AuthorizationRequired(msg))
118            }
119            "https://didcomm.org/basicmessage/2.0/message" => {
120                let msg: BasicMessage =
121                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
122                        Error::SerializationError(format!("Failed to parse BasicMessage: {}", e))
123                    })?;
124                Ok(TapMessage::BasicMessage(msg))
125            }
126            "https://tap.rsvp/schema/1.0#Cancel" => {
127                let msg: Cancel = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
128                    Error::SerializationError(format!("Failed to parse Cancel: {}", e))
129                })?;
130                Ok(TapMessage::Cancel(msg))
131            }
132            "https://tap.rsvp/schema/1.0#ConfirmRelationship" => {
133                let msg: ConfirmRelationship = serde_json::from_value(plain_msg.body.clone())
134                    .map_err(|e| {
135                        Error::SerializationError(format!(
136                            "Failed to parse ConfirmRelationship: {}",
137                            e
138                        ))
139                    })?;
140                Ok(TapMessage::ConfirmRelationship(msg))
141            }
142            "https://tap.rsvp/schema/1.0#Connect" => {
143                let msg: Connect = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
144                    Error::SerializationError(format!("Failed to parse Connect: {}", e))
145                })?;
146                Ok(TapMessage::Connect(msg))
147            }
148            "https://didcomm.org/present-proof/3.0/presentation" => {
149                let msg: DIDCommPresentation = serde_json::from_value(plain_msg.body.clone())
150                    .map_err(|e| {
151                        Error::SerializationError(format!(
152                            "Failed to parse DIDCommPresentation: {}",
153                            e
154                        ))
155                    })?;
156                Ok(TapMessage::DIDCommPresentation(msg))
157            }
158            "https://tap.rsvp/schema/1.0#Error" => {
159                let msg: ErrorBody =
160                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
161                        Error::SerializationError(format!("Failed to parse Error: {}", e))
162                    })?;
163                Ok(TapMessage::Error(msg))
164            }
165            "https://tap.rsvp/schema/1.0#OutOfBand" => {
166                let msg: OutOfBand =
167                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
168                        Error::SerializationError(format!("Failed to parse OutOfBand: {}", e))
169                    })?;
170                Ok(TapMessage::OutOfBand(msg))
171            }
172            "https://tap.rsvp/schema/1.0#Payment" => {
173                let msg: Payment = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
174                    Error::SerializationError(format!("Failed to parse Payment: {}", e))
175                })?;
176                Ok(TapMessage::Payment(msg))
177            }
178            "https://tap.rsvp/schema/1.0#Presentation" => {
179                let msg: Presentation =
180                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
181                        Error::SerializationError(format!("Failed to parse Presentation: {}", e))
182                    })?;
183                Ok(TapMessage::Presentation(msg))
184            }
185            "https://tap.rsvp/schema/1.0#Reject" => {
186                let msg: Reject = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
187                    Error::SerializationError(format!("Failed to parse Reject: {}", e))
188                })?;
189                Ok(TapMessage::Reject(msg))
190            }
191            "https://tap.rsvp/schema/1.0#RemoveAgent" => {
192                let msg: RemoveAgent =
193                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
194                        Error::SerializationError(format!("Failed to parse RemoveAgent: {}", e))
195                    })?;
196                Ok(TapMessage::RemoveAgent(msg))
197            }
198            "https://tap.rsvp/schema/1.0#ReplaceAgent" => {
199                let msg: ReplaceAgent =
200                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
201                        Error::SerializationError(format!("Failed to parse ReplaceAgent: {}", e))
202                    })?;
203                Ok(TapMessage::ReplaceAgent(msg))
204            }
205            "https://tap.rsvp/schema/1.0#RequestPresentation" => {
206                let msg: RequestPresentation = serde_json::from_value(plain_msg.body.clone())
207                    .map_err(|e| {
208                        Error::SerializationError(format!(
209                            "Failed to parse RequestPresentation: {}",
210                            e
211                        ))
212                    })?;
213                Ok(TapMessage::RequestPresentation(msg))
214            }
215            "https://tap.rsvp/schema/1.0#Revert" => {
216                let msg: Revert = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
217                    Error::SerializationError(format!("Failed to parse Revert: {}", e))
218                })?;
219                Ok(TapMessage::Revert(msg))
220            }
221            "https://tap.rsvp/schema/1.0#Settle" => {
222                let msg: Settle = serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
223                    Error::SerializationError(format!("Failed to parse Settle: {}", e))
224                })?;
225                Ok(TapMessage::Settle(msg))
226            }
227            "https://tap.rsvp/schema/1.0#Transfer" => {
228                let msg: Transfer =
229                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
230                        Error::SerializationError(format!("Failed to parse Transfer: {}", e))
231                    })?;
232                Ok(TapMessage::Transfer(msg))
233            }
234            "https://tap.rsvp/schema/1.0#UpdateParty" => {
235                let msg: UpdateParty =
236                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
237                        Error::SerializationError(format!("Failed to parse UpdateParty: {}", e))
238                    })?;
239                Ok(TapMessage::UpdateParty(msg))
240            }
241            "https://tap.rsvp/schema/1.0#UpdatePolicies" => {
242                let msg: UpdatePolicies =
243                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
244                        Error::SerializationError(format!("Failed to parse UpdatePolicies: {}", e))
245                    })?;
246                Ok(TapMessage::UpdatePolicies(msg))
247            }
248            "https://didcomm.org/trust-ping/2.0/ping" => {
249                let msg: TrustPing =
250                    serde_json::from_value(plain_msg.body.clone()).map_err(|e| {
251                        Error::SerializationError(format!("Failed to parse TrustPing: {}", e))
252                    })?;
253                Ok(TapMessage::TrustPing(msg))
254            }
255            "https://didcomm.org/trust-ping/2.0/ping-response" => {
256                let msg: TrustPingResponse = serde_json::from_value(plain_msg.body.clone())
257                    .map_err(|e| {
258                        Error::SerializationError(format!(
259                            "Failed to parse TrustPingResponse: {}",
260                            e
261                        ))
262                    })?;
263                Ok(TapMessage::TrustPingResponse(msg))
264            }
265            _ => Err(Error::Validation(format!(
266                "Unknown message type: {}",
267                message_type
268            ))),
269        }
270    }
271
272    /// Get the message type string for this TapMessage
273    pub fn message_type(&self) -> &'static str {
274        match self {
275            TapMessage::AddAgents(_) => "https://tap.rsvp/schema/1.0#AddAgents",
276            TapMessage::Authorize(_) => "https://tap.rsvp/schema/1.0#Authorize",
277            TapMessage::AuthorizationRequired(_) => {
278                "https://tap.rsvp/schema/1.0#AuthorizationRequired"
279            }
280            TapMessage::BasicMessage(_) => "https://didcomm.org/basicmessage/2.0/message",
281            TapMessage::Cancel(_) => "https://tap.rsvp/schema/1.0#Cancel",
282            TapMessage::ConfirmRelationship(_) => "https://tap.rsvp/schema/1.0#ConfirmRelationship",
283            TapMessage::Connect(_) => "https://tap.rsvp/schema/1.0#Connect",
284            TapMessage::DIDCommPresentation(_) => {
285                "https://didcomm.org/present-proof/3.0/presentation"
286            }
287            TapMessage::Error(_) => "https://tap.rsvp/schema/1.0#Error",
288            TapMessage::OutOfBand(_) => "https://tap.rsvp/schema/1.0#OutOfBand",
289            TapMessage::Payment(_) => "https://tap.rsvp/schema/1.0#Payment",
290            TapMessage::Presentation(_) => "https://tap.rsvp/schema/1.0#Presentation",
291            TapMessage::Reject(_) => "https://tap.rsvp/schema/1.0#Reject",
292            TapMessage::RemoveAgent(_) => "https://tap.rsvp/schema/1.0#RemoveAgent",
293            TapMessage::ReplaceAgent(_) => "https://tap.rsvp/schema/1.0#ReplaceAgent",
294            TapMessage::RequestPresentation(_) => "https://tap.rsvp/schema/1.0#RequestPresentation",
295            TapMessage::Revert(_) => "https://tap.rsvp/schema/1.0#Revert",
296            TapMessage::Settle(_) => "https://tap.rsvp/schema/1.0#Settle",
297            TapMessage::Transfer(_) => "https://tap.rsvp/schema/1.0#Transfer",
298            TapMessage::TrustPing(_) => "https://didcomm.org/trust-ping/2.0/ping",
299            TapMessage::TrustPingResponse(_) => "https://didcomm.org/trust-ping/2.0/ping-response",
300            TapMessage::UpdateParty(_) => "https://tap.rsvp/schema/1.0#UpdateParty",
301            TapMessage::UpdatePolicies(_) => "https://tap.rsvp/schema/1.0#UpdatePolicies",
302        }
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use serde_json::json;
310
311    #[test]
312    fn test_parse_transfer_body() {
313        let body = json!({
314            "@type": "https://tap.rsvp/schema/1.0#Transfer",
315            "transaction_id": "test-tx-123",
316            "asset": "eip155:1/slip44:60",
317            "originator": {
318                "@id": "did:example:alice"
319            },
320            "amount": "100",
321            "agents": [],
322            "metadata": {}
323        });
324
325        match serde_json::from_value::<Transfer>(body.clone()) {
326            Ok(transfer) => {
327                println!("Successfully parsed Transfer: {:?}", transfer);
328                assert_eq!(transfer.amount, "100");
329            }
330            Err(e) => {
331                panic!("Failed to parse Transfer: {}", e);
332            }
333        }
334    }
335
336    #[test]
337    fn test_from_plain_message_transfer() {
338        let plain_msg = PlainMessage {
339            id: "test-123".to_string(),
340            typ: "application/didcomm-plain+json".to_string(),
341            type_: "https://tap.rsvp/schema/1.0#Transfer".to_string(),
342            body: json!({
343                "@type": "https://tap.rsvp/schema/1.0#Transfer",
344                "transaction_id": "test-tx-456",
345                "asset": "eip155:1/slip44:60",
346                "originator": {
347                    "@id": "did:example:alice"
348                },
349                "amount": "100",
350                "agents": [],
351                "metadata": {}
352            }),
353            from: "did:example:alice".to_string(),
354            to: vec!["did:example:bob".to_string()],
355            thid: None,
356            pthid: None,
357            created_time: Some(1234567890),
358            expires_time: None,
359            from_prior: None,
360            attachments: None,
361            extra_headers: Default::default(),
362        };
363
364        let tap_msg = TapMessage::from_plain_message(&plain_msg).unwrap();
365
366        match tap_msg {
367            TapMessage::Transfer(transfer) => {
368                assert_eq!(transfer.amount, "100");
369                assert_eq!(
370                    transfer.originator.as_ref().unwrap().id,
371                    "did:example:alice"
372                );
373            }
374            _ => panic!("Expected Transfer message"),
375        }
376    }
377
378    #[test]
379    fn test_message_type() {
380        let transfer = Transfer {
381            asset: "eip155:1/slip44:60".parse().unwrap(),
382            originator: Some(crate::message::Party::new("did:example:alice")),
383            beneficiary: None,
384            amount: "100".to_string(),
385            agents: vec![],
386            memo: None,
387            settlement_id: None,
388            connection_id: None,
389            transaction_id: Some("tx-123".to_string()),
390            metadata: Default::default(),
391        };
392
393        let tap_msg = TapMessage::Transfer(transfer);
394        assert_eq!(
395            tap_msg.message_type(),
396            "https://tap.rsvp/schema/1.0#Transfer"
397        );
398    }
399}