tap_msg/
derive.rs

1/// Macro to implement TapMessage for a struct.
2///
3/// This macro implements the TapMessage trait for a struct that implements TapMessageBody.
4/// It supports different message field structures with specialized variants.
5///
6/// # Variants
7///
8/// ## Basic (Standard transaction_id field)
9///
10/// Use this variant for message types with a required `transaction_id: String` field:
11///
12/// ```ignore
13/// // Example usage - this won't be run as a test
14/// use tap_msg::impl_tap_message;
15/// impl_tap_message!(Transfer);
16/// ```
17///
18/// ## Optional Transaction ID
19///
20/// Use this variant for message types with an optional `transaction_id: Option<String>` field:
21///
22/// ```ignore
23/// // Example usage - this won't be run as a test
24/// use tap_msg::impl_tap_message;
25/// impl_tap_message!(Presentation, optional_transaction_id);
26/// ```
27///
28/// ## Thread-based Messages
29///
30/// Use this variant for message types with a `thid: Option<String>` field but no transaction_id:
31///
32/// ```ignore
33/// // Example usage - this won't be run as a test
34/// use tap_msg::impl_tap_message;
35/// impl_tap_message!(DIDCommPresentation, thread_based);
36/// ```
37///
38/// ## Generated ID
39///
40/// Use this variant for message types with neither transaction_id nor thread_id fields:
41///
42/// ```ignore
43/// // Example usage - this won't be run as a test
44/// use tap_msg::impl_tap_message;
45/// impl_tap_message!(ErrorBody, generated_id);
46/// ```
47///
48/// # Complete Example
49///
50/// ```ignore
51/// // Example usage - this won't be run as a test
52/// use tap_msg::impl_tap_message;
53/// use tap_msg::message::tap_message_trait::{TapMessageBody, TapMessage};
54/// use tap_msg::error::Result;
55/// use serde::{Serialize, Deserialize};
56/// use crate::didcomm::PlainMessage;
57///
58/// // Your struct that implements TapMessageBody
59/// #[derive(Serialize, Deserialize)]
60/// struct MyMessage {
61///     transaction_id: String,
62///     // other fields...
63/// }
64///
65/// impl TapMessageBody for MyMessage {
66///     fn validate(&self) -> Result<()> {
67///         Ok(())
68///     }
69///
70///     // Note: This is a static method, not an instance method
71///     fn message_type() -> &'static str {
72///         "my-message"
73///     }
74///
75///     fn to_didcomm(&self, from_did: &str) -> Result<PlainMessage> {
76///         // Implementation omitted
77///         unimplemented!()
78///     }
79/// }
80///
81/// // Implement TapMessage trait
82/// impl_tap_message!(MyMessage);
83/// ```
84#[macro_export]
85macro_rules! impl_tap_message {
86    // For types with a required transaction_id field (most common case)
87    ($type:ty) => {
88        impl $crate::message::tap_message_trait::TapMessage for $type {
89            fn validate(&self) -> $crate::error::Result<()> {
90                <Self as $crate::message::tap_message_trait::TapMessageBody>::validate(self)
91            }
92            fn is_tap_message(&self) -> bool {
93                false
94            }
95            fn get_tap_type(&self) -> Option<String> {
96                Some(
97                    <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
98                        .to_string(),
99                )
100            }
101            fn body_as<T: $crate::message::tap_message_trait::TapMessageBody>(
102                &self,
103            ) -> $crate::error::Result<T> {
104                unimplemented!()
105            }
106            fn get_all_participants(&self) -> Vec<String> {
107                Vec::new()
108            }
109            fn create_reply<T: $crate::message::tap_message_trait::TapMessageBody>(
110                &self,
111                body: &T,
112                creator_did: &str,
113            ) -> $crate::error::Result<$crate::didcomm::PlainMessage> {
114                // Create the base message with creator as sender
115                let mut message = body.to_didcomm(creator_did)?;
116
117                // Set the thread ID to maintain the conversation thread
118                if let Some(thread_id) = self.thread_id() {
119                    message.thid = Some(thread_id.to_string());
120                } else {
121                    // If no thread ID exists, use the original message ID as the thread ID
122                    message.thid = Some(self.message_id().to_string());
123                }
124
125                // Set the parent thread ID if this thread is part of a larger transaction
126                if let Some(parent_thread_id) = self.parent_thread_id() {
127                    message.pthid = Some(parent_thread_id.to_string());
128                }
129
130                Ok(message)
131            }
132            fn message_type(&self) -> &'static str {
133                <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
134            }
135            fn thread_id(&self) -> Option<&str> {
136                // for types with transaction_id
137                Some(&self.transaction_id)
138            }
139            fn parent_thread_id(&self) -> Option<&str> {
140                None
141            }
142            fn message_id(&self) -> &str {
143                &self.transaction_id
144            }
145        }
146    };
147
148    // For types with an optional transaction_id field
149    ($type:ty, optional_transaction_id) => {
150        impl $crate::message::tap_message_trait::TapMessage for $type {
151            fn validate(&self) -> $crate::error::Result<()> {
152                <Self as $crate::message::tap_message_trait::TapMessageBody>::validate(self)
153            }
154            fn is_tap_message(&self) -> bool {
155                false
156            }
157            fn get_tap_type(&self) -> Option<String> {
158                Some(
159                    <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
160                        .to_string(),
161                )
162            }
163            fn body_as<T: $crate::message::tap_message_trait::TapMessageBody>(
164                &self,
165            ) -> $crate::error::Result<T> {
166                unimplemented!()
167            }
168            fn get_all_participants(&self) -> Vec<String> {
169                Vec::new()
170            }
171            fn create_reply<T: $crate::message::tap_message_trait::TapMessageBody>(
172                &self,
173                body: &T,
174                creator_did: &str,
175            ) -> $crate::error::Result<$crate::didcomm::PlainMessage> {
176                // Create the base message with creator as sender
177                let mut message = body.to_didcomm(creator_did)?;
178
179                // Set the thread ID to maintain the conversation thread
180                if let Some(thread_id) = self.thread_id() {
181                    message.thid = Some(thread_id.to_string());
182                } else {
183                    // If no thread ID exists, use the original message ID as the thread ID
184                    message.thid = Some(self.message_id().to_string());
185                }
186
187                // Set the parent thread ID if this thread is part of a larger transaction
188                if let Some(parent_thread_id) = self.parent_thread_id() {
189                    message.pthid = Some(parent_thread_id.to_string());
190                }
191
192                Ok(message)
193            }
194            fn message_type(&self) -> &'static str {
195                <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
196            }
197            fn thread_id(&self) -> Option<&str> {
198                self.transaction_id.as_deref()
199            }
200            fn parent_thread_id(&self) -> Option<&str> {
201                None
202            }
203            fn message_id(&self) -> &str {
204                if let Some(ref id) = self.transaction_id {
205                    id
206                } else {
207                    &self.id
208                }
209            }
210        }
211    };
212
213    // For types with a thread_id field instead of transaction_id
214    ($type:ty, thread_based) => {
215        impl $crate::message::tap_message_trait::TapMessage for $type {
216            fn validate(&self) -> $crate::error::Result<()> {
217                <Self as $crate::message::tap_message_trait::TapMessageBody>::validate(self)
218            }
219            fn is_tap_message(&self) -> bool {
220                false
221            }
222            fn get_tap_type(&self) -> Option<String> {
223                Some(
224                    <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
225                        .to_string(),
226                )
227            }
228            fn body_as<T: $crate::message::tap_message_trait::TapMessageBody>(
229                &self,
230            ) -> $crate::error::Result<T> {
231                unimplemented!()
232            }
233            fn get_all_participants(&self) -> Vec<String> {
234                Vec::new()
235            }
236            fn create_reply<T: $crate::message::tap_message_trait::TapMessageBody>(
237                &self,
238                body: &T,
239                creator_did: &str,
240            ) -> $crate::error::Result<$crate::didcomm::PlainMessage> {
241                // Create the base message with creator as sender
242                let mut message = body.to_didcomm(creator_did)?;
243
244                // Set the thread ID to maintain the conversation thread
245                if let Some(thread_id) = self.thread_id() {
246                    message.thid = Some(thread_id.to_string());
247                } else {
248                    // If no thread ID exists, use the original message ID as the thread ID
249                    message.thid = Some(self.message_id().to_string());
250                }
251
252                Ok(message)
253            }
254            fn message_type(&self) -> &'static str {
255                <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
256            }
257            fn thread_id(&self) -> Option<&str> {
258                self.thid.as_deref()
259            }
260            fn parent_thread_id(&self) -> Option<&str> {
261                None
262            }
263            fn message_id(&self) -> &str {
264                if let Some(ref thid) = self.thid {
265                    thid
266                } else {
267                    &self.id
268                }
269            }
270        }
271    };
272
273    // For types with neither transaction_id nor thread_id (generated ID)
274    ($type:ty, generated_id) => {
275        impl $crate::message::tap_message_trait::TapMessage for $type {
276            fn validate(&self) -> $crate::error::Result<()> {
277                <Self as $crate::message::tap_message_trait::TapMessageBody>::validate(self)
278            }
279            fn is_tap_message(&self) -> bool {
280                false
281            }
282            fn get_tap_type(&self) -> Option<String> {
283                Some(
284                    <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
285                        .to_string(),
286                )
287            }
288            fn body_as<T: $crate::message::tap_message_trait::TapMessageBody>(
289                &self,
290            ) -> $crate::error::Result<T> {
291                unimplemented!()
292            }
293            fn get_all_participants(&self) -> Vec<String> {
294                Vec::new()
295            }
296            fn create_reply<T: $crate::message::tap_message_trait::TapMessageBody>(
297                &self,
298                body: &T,
299                creator_did: &str,
300            ) -> $crate::error::Result<$crate::didcomm::PlainMessage> {
301                // Create the base message with creator as sender
302                let message = body.to_didcomm(creator_did)?;
303
304                // For types without thread/transaction ID, we don't set thread ID on replies
305
306                Ok(message)
307            }
308            fn message_type(&self) -> &'static str {
309                <Self as $crate::message::tap_message_trait::TapMessageBody>::message_type()
310            }
311            fn thread_id(&self) -> Option<&str> {
312                None
313            }
314            fn parent_thread_id(&self) -> Option<&str> {
315                None
316            }
317            fn message_id(&self) -> &str {
318                // For types without an ID field, we'll use a static string
319                // This isn't ideal but it satisfies the API contract
320                // In real usage, these message types should be wrapped in a TapMessage
321                // implementation that provides a proper ID
322                static FALLBACK_ID: &str = "00000000-0000-0000-0000-000000000000";
323                FALLBACK_ID
324            }
325        }
326    };
327}