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