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}