1use proc_macro::TokenStream;
7use proc_macro2::TokenStream as TokenStream2;
8use quote::quote;
9use syn::{parse_macro_input, Data, DeriveInput, Field, Fields};
10
11#[proc_macro_derive(TapMessage, attributes(tap))]
91pub fn derive_tap_message(input: TokenStream) -> TokenStream {
92 let input = parse_macro_input!(input as DeriveInput);
93 let expanded = impl_tap_message(&input);
94 TokenStream::from(expanded)
95}
96
97#[proc_macro_derive(TapMessageBody, attributes(tap))]
98pub fn derive_tap_message_body(input: TokenStream) -> TokenStream {
99 let input = parse_macro_input!(input as DeriveInput);
100 let expanded = impl_tap_message_body_only(&input);
101 TokenStream::from(expanded)
102}
103
104fn impl_tap_message(input: &DeriveInput) -> TokenStream2 {
105 let name = &input.ident;
106 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
107
108 let fields = match &input.data {
109 Data::Struct(data) => match &data.fields {
110 Fields::Named(fields) => &fields.named,
111 _ => panic!("TapMessage can only be derived for structs with named fields"),
112 },
113 _ => panic!("TapMessage can only be derived for structs"),
114 };
115
116 let field_info = analyze_fields(fields, &input.attrs);
117
118 let is_internal = std::env::var("CARGO_CRATE_NAME").unwrap_or_default() == "tap_msg";
120
121 let tap_message_body_impl = if field_info.message_type.is_some() {
122 impl_tap_message_body_trait(
123 name,
124 &field_info,
125 &impl_generics,
126 &ty_generics,
127 where_clause,
128 is_internal,
129 )
130 } else {
131 quote! {}
132 };
133
134 let tap_message_impl = impl_tap_message_trait(
135 name,
136 &field_info,
137 &impl_generics,
138 &ty_generics,
139 where_clause,
140 is_internal,
141 );
142
143 let message_context_impl = impl_message_context_trait(
144 name,
145 &field_info,
146 &impl_generics,
147 &ty_generics,
148 where_clause,
149 is_internal,
150 );
151
152 let authorizable_impl = if field_info.is_authorizable {
153 impl_authorizable_trait(
154 name,
155 &field_info,
156 &impl_generics,
157 &ty_generics,
158 where_clause,
159 is_internal,
160 )
161 } else {
162 quote! {}
163 };
164
165 let transaction_impl = if field_info.is_transactable {
166 impl_transaction_trait(
167 name,
168 &field_info,
169 &impl_generics,
170 &ty_generics,
171 where_clause,
172 is_internal,
173 )
174 } else {
175 quote! {}
176 };
177
178 let connectable_impl = if field_info.connection_id_field.is_some() || field_info.is_initiator {
179 impl_connectable_trait(
180 name,
181 &field_info,
182 &impl_generics,
183 &ty_generics,
184 where_clause,
185 is_internal,
186 )
187 } else {
188 quote! {}
189 };
190
191 quote! {
192 #tap_message_impl
193 #message_context_impl
194 #tap_message_body_impl
195 #authorizable_impl
196 #transaction_impl
197 #connectable_impl
198 }
199}
200
201fn impl_connectable_trait(
202 name: &syn::Ident,
203 field_info: &FieldInfo,
204 impl_generics: &syn::ImplGenerics,
205 ty_generics: &syn::TypeGenerics,
206 where_clause: Option<&syn::WhereClause>,
207 is_internal: bool,
208) -> TokenStream2 {
209 let crate_path = if is_internal {
210 quote! { crate }
211 } else {
212 quote! { ::tap_msg }
213 };
214
215 if let Some(ref conn_field) = field_info.connection_id_field {
216 quote! {
218 impl #impl_generics #crate_path::message::tap_message_trait::Connectable for #name #ty_generics #where_clause {
219 fn with_connection(&mut self, connection_id: &str) -> &mut Self {
220 self.#conn_field = Some(connection_id.to_string());
221 self
222 }
223
224 fn has_connection(&self) -> bool {
225 self.#conn_field.is_some()
226 }
227
228 fn connection_id(&self) -> Option<&str> {
229 self.#conn_field.as_deref()
230 }
231 }
232 }
233 } else {
234 quote! {
236 impl #impl_generics #crate_path::message::tap_message_trait::Connectable for #name #ty_generics #where_clause {
237 fn with_connection(&mut self, _connection_id: &str) -> &mut Self {
238 self
240 }
241
242 fn has_connection(&self) -> bool {
243 false
244 }
245
246 fn connection_id(&self) -> Option<&str> {
247 None
248 }
249 }
250 }
251 }
252}
253
254#[derive(Debug)]
255struct FieldInfo {
256 participant_fields: Vec<syn::Ident>,
257 optional_participant_fields: Vec<syn::Ident>,
258 participant_list_fields: Vec<syn::Ident>,
259 transaction_id_field: Option<syn::Ident>,
260 optional_transaction_id_field: Option<syn::Ident>,
261 thread_id_field: Option<syn::Ident>,
262 optional_thread_id_field: Option<syn::Ident>,
263 connection_id_field: Option<syn::Ident>,
264 has_generated_id: bool,
265 message_type: Option<String>,
266 is_initiator: bool,
267 is_authorizable: bool,
268 is_transactable: bool,
269 generate_builder: bool,
270 custom_validation: bool,
271}
272
273fn analyze_fields(
274 fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
275 struct_attrs: &[syn::Attribute],
276) -> FieldInfo {
277 let mut field_info = FieldInfo {
278 participant_fields: Vec::new(),
279 optional_participant_fields: Vec::new(),
280 participant_list_fields: Vec::new(),
281 transaction_id_field: None,
282 optional_transaction_id_field: None,
283 thread_id_field: None,
284 optional_thread_id_field: None,
285 connection_id_field: None,
286 has_generated_id: false,
287 message_type: None,
288 is_initiator: false,
289 is_authorizable: false,
290 is_transactable: false,
291 generate_builder: false,
292 custom_validation: false,
293 };
294
295 for attr in struct_attrs {
297 if attr.path().is_ident("tap") {
298 let _ = attr.parse_nested_meta(|meta| {
299 if meta.path.is_ident("generated_id") {
300 field_info.has_generated_id = true;
301 } else if meta.path.is_ident("message_type") {
302 if let Ok(lit) = meta.value() {
303 if let Ok(lit_str) = lit.parse::<syn::LitStr>() {
304 field_info.message_type = Some(lit_str.value());
305 }
306 }
307 } else if meta.path.is_ident("initiator") {
308 field_info.is_initiator = true;
309 } else if meta.path.is_ident("authorizable") {
310 field_info.is_authorizable = true;
311 } else if meta.path.is_ident("transactable") {
312 field_info.is_transactable = true;
313 } else if meta.path.is_ident("builder") {
314 field_info.generate_builder = true;
315 } else if meta.path.is_ident("custom_validation") {
316 field_info.custom_validation = true;
317 }
318 Ok(())
319 });
320 }
321 }
322
323 for field in fields {
324 let field_name = field.ident.as_ref().expect("Field must have a name");
325
326 for attr in &field.attrs {
327 if attr.path().is_ident("tap") {
328 let _ = attr.parse_nested_meta(|meta| {
329 if meta.path.is_ident("participant") {
330 if is_optional_type(&field.ty) {
332 field_info
333 .optional_participant_fields
334 .push(field_name.clone());
335 } else {
336 field_info.participant_fields.push(field_name.clone());
337 }
338 } else if meta.path.is_ident("participant_list") {
339 field_info.participant_list_fields.push(field_name.clone());
340 } else if meta.path.is_ident("transaction_id") {
341 field_info.transaction_id_field = Some(field_name.clone());
342 } else if meta.path.is_ident("optional_transaction_id") {
343 field_info.optional_transaction_id_field = Some(field_name.clone());
344 } else if meta.path.is_ident("thread_id") {
345 if is_optional_type(&field.ty) {
347 field_info.optional_thread_id_field = Some(field_name.clone());
348 } else {
349 field_info.thread_id_field = Some(field_name.clone());
350 }
351 } else if meta.path.is_ident("connection_id") {
352 field_info.connection_id_field = Some(field_name.clone());
353 } else if meta.path.is_ident("generated_id") {
354 field_info.has_generated_id = true;
355 }
356 Ok(())
357 });
358 }
359 }
360 }
361
362 field_info
363}
364
365fn is_optional_type(ty: &syn::Type) -> bool {
366 if let syn::Type::Path(type_path) = ty {
367 if let Some(segment) = type_path.path.segments.last() {
368 return segment.ident == "Option";
369 }
370 }
371 false
372}
373
374fn impl_tap_message_trait(
375 name: &syn::Ident,
376 field_info: &FieldInfo,
377 impl_generics: &syn::ImplGenerics,
378 ty_generics: &syn::TypeGenerics,
379 where_clause: Option<&syn::WhereClause>,
380 is_internal: bool,
381) -> TokenStream2 {
382 let thread_id_impl = generate_thread_id_impl(field_info);
383 let message_id_impl = generate_message_id_impl(field_info);
384 let get_all_participants_impl = generate_get_all_participants_impl(field_info);
385
386 let crate_path = if is_internal {
387 quote! { crate }
388 } else {
389 quote! { ::tap_msg }
390 };
391
392 quote! {
395 impl #impl_generics #crate_path::message::tap_message_trait::TapMessage for #name #ty_generics #where_clause {
396 fn validate(&self) -> #crate_path::error::Result<()> {
397 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::validate(self)
398 }
399
400 fn is_tap_message(&self) -> bool {
401 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type()
402 .starts_with("https://tap.rsvp/schema/1.0#")
403 }
404
405 fn get_tap_type(&self) -> Option<String> {
406 Some(
407 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type()
408 .to_string(),
409 )
410 }
411
412 fn body_as<T: #crate_path::message::tap_message_trait::TapMessageBody>(
413 &self,
414 ) -> #crate_path::error::Result<T> {
415 unimplemented!()
416 }
417
418 fn get_all_participants(&self) -> Vec<String> {
419 use #crate_path::message::agent::TapParticipant;
421 #get_all_participants_impl
422 }
423
424 fn create_reply<T: #crate_path::message::tap_message_trait::TapMessageBody>(
425 &self,
426 body: &T,
427 creator_did: &str,
428 ) -> #crate_path::error::Result<#crate_path::didcomm::PlainMessage> {
429 let mut message = body.to_didcomm(creator_did)?;
431
432 if let Some(thread_id) = self.thread_id() {
436 message.thid = Some(thread_id.to_string());
437 } else {
438 message.thid = Some(self.message_id().to_string());
440 }
441
442 if let Some(parent_thread_id) = self.parent_thread_id() {
444 message.pthid = Some(parent_thread_id.to_string());
445 }
446
447 Ok(message)
448 }
449
450
451 fn thread_id(&self) -> Option<&str> {
452 #thread_id_impl
453 }
454
455 fn parent_thread_id(&self) -> Option<&str> {
456 None
457 }
458
459 fn message_id(&self) -> &str {
460 #message_id_impl
461 }
462 }
463 }
464}
465
466fn impl_message_context_trait(
467 name: &syn::Ident,
468 field_info: &FieldInfo,
469 impl_generics: &syn::ImplGenerics,
470 ty_generics: &syn::TypeGenerics,
471 where_clause: Option<&syn::WhereClause>,
472 is_internal: bool,
473) -> TokenStream2 {
474 let participant_dids_impl = generate_participant_dids_impl(field_info);
475 let transaction_context_impl = generate_transaction_context_impl(field_info, is_internal);
476
477 let crate_path = if is_internal {
478 quote! { crate }
479 } else {
480 quote! { ::tap_msg }
481 };
482
483 quote! {
484 impl #impl_generics #crate_path::message::MessageContext for #name #ty_generics #where_clause {
485 fn participant_dids(&self) -> Vec<String> {
486 use #crate_path::message::agent::TapParticipant;
488 #participant_dids_impl
489 }
490
491 fn transaction_context(&self) -> Option<#crate_path::message::TransactionContext> {
492 #transaction_context_impl
493 }
494 }
495 }
496}
497
498fn generate_participant_dids_impl(field_info: &FieldInfo) -> TokenStream2 {
499 let mut did_extracts = Vec::new();
500
501 for field in &field_info.participant_fields {
503 did_extracts.push(quote! {
504 dids.push(self.#field.id().to_string());
506 });
507 }
508
509 for field in &field_info.optional_participant_fields {
511 did_extracts.push(quote! {
512 if let Some(ref participant) = self.#field {
513 dids.push(participant.id().to_string());
514 }
515 });
516 }
517
518 for field in &field_info.participant_list_fields {
520 did_extracts.push(quote! {
521 for participant in &self.#field {
522 dids.push(participant.id().to_string());
523 }
524 });
525 }
526
527 quote! {
528 let mut dids = Vec::new();
529 #(#did_extracts)*
530 dids
531 }
532}
533
534fn generate_get_all_participants_impl(field_info: &FieldInfo) -> TokenStream2 {
535 let mut participant_extracts = Vec::new();
536
537 for field in &field_info.participant_fields {
539 participant_extracts.push(quote! {
540 participants.push(self.#field.id().to_string());
541 });
542 }
543
544 for field in &field_info.optional_participant_fields {
546 participant_extracts.push(quote! {
547 if let Some(ref participant) = self.#field {
548 participants.push(participant.id().to_string());
549 }
550 });
551 }
552
553 for field in &field_info.participant_list_fields {
555 participant_extracts.push(quote! {
556 for participant in &self.#field {
557 participants.push(participant.id().to_string());
558 }
559 });
560 }
561
562 quote! {
563 let mut participants = Vec::new();
564 #(#participant_extracts)*
565 participants
566 }
567}
568
569fn generate_thread_id_impl(field_info: &FieldInfo) -> TokenStream2 {
570 if let Some(ref thread_field) = field_info.thread_id_field {
571 quote! { Some(&self.#thread_field) }
572 } else if let Some(ref opt_thread_field) = field_info.optional_thread_id_field {
573 quote! { self.#opt_thread_field.as_deref() }
574 } else if field_info.is_initiator {
575 quote! { None }
577 } else if let Some(ref tx_field) = field_info.transaction_id_field {
578 quote! { Some(&self.#tx_field) }
579 } else if let Some(ref opt_tx_field) = field_info.optional_transaction_id_field {
580 quote! { self.#opt_tx_field.as_deref() }
581 } else {
582 quote! { None }
583 }
584}
585
586fn generate_message_id_impl(field_info: &FieldInfo) -> TokenStream2 {
587 if let Some(tx_field) = &field_info.transaction_id_field {
588 quote! { &self.#tx_field }
589 } else if let Some(thread_field) = &field_info.thread_id_field {
590 quote! { &self.#thread_field }
591 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
592 quote! {
593 self.#opt_tx_field.as_deref().unwrap_or("")
594 }
595 } else {
596 quote! {
597 static FALLBACK_ID: &str = "00000000-0000-0000-0000-000000000000";
600 FALLBACK_ID
601 }
602 }
603}
604
605fn generate_transaction_context_impl(field_info: &FieldInfo, is_internal: bool) -> TokenStream2 {
606 let crate_path = if is_internal {
607 quote! { crate }
608 } else {
609 quote! { ::tap_msg }
610 };
611
612 if let Some(tx_field) = &field_info.transaction_id_field {
613 quote! {
614 Some(#crate_path::message::TransactionContext::new(
615 self.#tx_field.clone(),
616 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type().to_string(),
617 ))
618 }
619 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
620 quote! {
621 self.#opt_tx_field.as_ref().map(|tx_id| {
622 #crate_path::message::TransactionContext::new(
623 tx_id.clone(),
624 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type().to_string(),
625 )
626 })
627 }
628 } else {
629 quote! { None }
630 }
631}
632
633fn impl_tap_message_body_trait(
634 name: &syn::Ident,
635 field_info: &FieldInfo,
636 impl_generics: &syn::ImplGenerics,
637 ty_generics: &syn::TypeGenerics,
638 where_clause: Option<&syn::WhereClause>,
639 is_internal: bool,
640) -> TokenStream2 {
641 let crate_path = if is_internal {
642 quote! { crate }
643 } else {
644 quote! { ::tap_msg }
645 };
646
647 let message_type = field_info
648 .message_type
649 .as_ref()
650 .expect("message_type attribute is required for TapMessageBody");
651
652 let validate_impl = if field_info.custom_validation {
653 let method_name = syn::Ident::new(
655 &format!("validate_{}", name.to_string().to_lowercase()),
656 name.span(),
657 );
658 quote! {
659 self.#method_name()
660 }
661 } else {
662 quote! {
663 Ok(())
665 }
666 };
667
668 let to_didcomm_impl = generate_to_didcomm_impl(field_info, is_internal);
669
670 quote! {
671 impl #impl_generics #crate_path::message::tap_message_trait::TapMessageBody for #name #ty_generics #where_clause {
672 fn message_type() -> &'static str {
673 #message_type
674 }
675
676 fn validate(&self) -> #crate_path::error::Result<()> {
677 #validate_impl
678 }
679
680 fn to_didcomm(&self, from_did: &str) -> #crate_path::error::Result<#crate_path::didcomm::PlainMessage> {
681 #to_didcomm_impl
682 }
683 }
684 }
685}
686
687fn generate_to_didcomm_impl(field_info: &FieldInfo, is_internal: bool) -> TokenStream2 {
688 let crate_path = if is_internal {
689 quote! { crate }
690 } else {
691 quote! { ::tap_msg }
692 };
693
694 let participant_extraction = if !field_info.participant_fields.is_empty()
696 || !field_info.optional_participant_fields.is_empty()
697 || !field_info.participant_list_fields.is_empty()
698 {
699 let mut extracts = Vec::new();
700
701 for field in &field_info.participant_fields {
703 extracts.push(quote! {
704 recipient_dids.push(self.#field.id().to_string());
705 });
706 }
707
708 for field in &field_info.optional_participant_fields {
710 extracts.push(quote! {
711 if let Some(ref participant) = self.#field {
712 recipient_dids.push(participant.id().to_string());
713 }
714 });
715 }
716
717 for field in &field_info.participant_list_fields {
719 extracts.push(quote! {
720 for participant in &self.#field {
721 recipient_dids.push(participant.id().to_string());
722 }
723 });
724 }
725
726 quote! {
727 let mut recipient_dids = Vec::new();
728 #(#extracts)*
729
730 recipient_dids.sort();
732 recipient_dids.dedup();
733 recipient_dids.retain(|did| did != from_did);
734 }
735 } else {
736 quote! {
737 let recipient_dids: Vec<String> = Vec::new();
738 }
739 };
740
741 let thread_assignment = if field_info.is_initiator {
743 if let Some(tx_field) = &field_info.transaction_id_field {
745 quote! {
746 thid: Some(self.#tx_field.clone()),
747 }
748 } else {
749 quote! {
750 thid: None,
751 }
752 }
753 } else if let Some(thread_field) = &field_info.thread_id_field {
754 quote! {
756 thid: Some(self.#thread_field.clone()),
757 }
758 } else if let Some(opt_thread_field) = &field_info.optional_thread_id_field {
759 quote! {
761 thid: self.#opt_thread_field.clone(),
762 }
763 } else if let Some(tx_field) = &field_info.transaction_id_field {
764 quote! {
766 thid: Some(self.#tx_field.clone()),
767 }
768 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
769 quote! {
770 thid: self.#opt_tx_field.clone(),
771 }
772 } else {
773 quote! {
774 thid: None,
775 }
776 };
777
778 let pthid_assignment = if let Some(conn_field) = &field_info.connection_id_field {
780 quote! {
781 pthid: self.#conn_field.clone(),
782 }
783 } else {
784 quote! {
785 pthid: None,
786 }
787 };
788
789 quote! {
790 use #crate_path::message::agent::TapParticipant;
792
793 let mut body_json = serde_json::to_value(self)
795 .map_err(|e| #crate_path::error::Error::SerializationError(e.to_string()))?;
796
797 if let Some(body_obj) = body_json.as_object_mut() {
799 body_obj.insert(
800 "@type".to_string(),
801 serde_json::Value::String(Self::message_type().to_string()),
802 );
803 }
804
805 #participant_extraction
807
808 let now = chrono::Utc::now().timestamp() as u64;
809
810 Ok(#crate_path::didcomm::PlainMessage {
812 id: uuid::Uuid::new_v4().to_string(),
813 typ: "application/didcomm-plain+json".to_string(),
814 type_: Self::message_type().to_string(),
815 body: body_json,
816 from: from_did.to_string(),
817 to: recipient_dids,
818 #thread_assignment
819 #pthid_assignment
820 created_time: Some(now),
821 expires_time: None,
822 extra_headers: std::collections::HashMap::new(),
823 from_prior: None,
824 attachments: None,
825 })
826 }
827}
828
829fn impl_tap_message_body_only(input: &DeriveInput) -> TokenStream2 {
830 let name = &input.ident;
831 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
832
833 let fields = match &input.data {
834 Data::Struct(data) => match &data.fields {
835 Fields::Named(fields) => &fields.named,
836 _ => panic!("TapMessageBody can only be derived for structs with named fields"),
837 },
838 _ => panic!("TapMessageBody can only be derived for structs"),
839 };
840
841 let field_info = analyze_fields(fields, &input.attrs);
842
843 let is_internal = std::env::var("CARGO_CRATE_NAME").unwrap_or_default() == "tap_msg";
845
846 if field_info.message_type.is_none() {
848 panic!("TapMessageBody derive macro requires #[tap(message_type = \"...\")] attribute");
849 }
850
851 impl_tap_message_body_trait(
852 name,
853 &field_info,
854 &impl_generics,
855 &ty_generics,
856 where_clause,
857 is_internal,
858 )
859}
860
861fn impl_authorizable_trait(
862 name: &syn::Ident,
863 field_info: &FieldInfo,
864 impl_generics: &syn::ImplGenerics,
865 ty_generics: &syn::TypeGenerics,
866 where_clause: Option<&syn::WhereClause>,
867 is_internal: bool,
868) -> TokenStream2 {
869 let crate_path = if is_internal {
870 quote! { crate }
871 } else {
872 quote! { ::tap_msg }
873 };
874
875 let tx_id_access = if let Some(ref tx_field) = field_info.transaction_id_field {
877 quote! { &self.#tx_field }
878 } else if let Some(ref thread_field) = field_info.thread_id_field {
879 quote! { &self.#thread_field }
880 } else {
881 quote! { "" }
882 };
883
884 quote! {
885 impl #impl_generics #crate_path::message::tap_message_trait::Authorizable for #name #ty_generics #where_clause {
886 fn authorize(
887 &self,
888 creator_did: &str,
889 settlement_address: Option<&str>,
890 expiry: Option<&str>,
891 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Authorize> {
892 let authorize = #crate_path::message::Authorize::with_all(#tx_id_access, settlement_address, expiry);
893 let original_message = self
894 .to_didcomm(creator_did)
895 .expect("Failed to create DIDComm message");
896 let reply = original_message
897 .create_reply(&authorize, creator_did)
898 .expect("Failed to create reply");
899 #crate_path::message::tap_message_trait::typed_plain_message(reply, authorize)
900 }
901
902 fn cancel(&self, creator_did: &str, by: &str, reason: Option<&str>) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Cancel> {
903 let cancel = if let Some(reason) = reason {
904 #crate_path::message::Cancel::with_reason(#tx_id_access, by, reason)
905 } else {
906 #crate_path::message::Cancel::new(#tx_id_access, by)
907 };
908 let original_message = self
909 .to_didcomm(creator_did)
910 .expect("Failed to create DIDComm message");
911 let reply = original_message
912 .create_reply(&cancel, creator_did)
913 .expect("Failed to create reply");
914 #crate_path::message::tap_message_trait::typed_plain_message(reply, cancel)
915 }
916
917 fn reject(&self, creator_did: &str, reason: &str) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Reject> {
918 let reject = #crate_path::message::Reject {
919 transaction_id: (#tx_id_access).to_string(),
920 reason: reason.to_string(),
921 };
922 let original_message = self
923 .to_didcomm(creator_did)
924 .expect("Failed to create DIDComm message");
925 let reply = original_message
926 .create_reply(&reject, creator_did)
927 .expect("Failed to create reply");
928 #crate_path::message::tap_message_trait::typed_plain_message(reply, reject)
929 }
930 }
931 }
932}
933
934fn impl_transaction_trait(
935 name: &syn::Ident,
936 field_info: &FieldInfo,
937 impl_generics: &syn::ImplGenerics,
938 ty_generics: &syn::TypeGenerics,
939 where_clause: Option<&syn::WhereClause>,
940 is_internal: bool,
941) -> TokenStream2 {
942 let crate_path = if is_internal {
943 quote! { crate }
944 } else {
945 quote! { ::tap_msg }
946 };
947
948 let tx_id_access = if let Some(ref tx_field) = field_info.transaction_id_field {
950 quote! { &self.#tx_field }
951 } else if let Some(ref thread_field) = field_info.thread_id_field {
952 quote! { &self.#thread_field }
953 } else {
954 quote! { "" }
955 };
956
957 quote! {
958 impl #impl_generics #crate_path::message::tap_message_trait::Transaction for #name #ty_generics #where_clause {
959 fn settle(
960 &self,
961 creator_did: &str,
962 settlement_id: &str,
963 amount: Option<&str>,
964 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Settle> {
965 let settle = #crate_path::message::Settle {
966 transaction_id: (#tx_id_access).to_string(),
967 settlement_id: settlement_id.to_string(),
968 amount: amount.map(|s| s.to_string()),
969 };
970 let original_message = self
971 .to_didcomm(creator_did)
972 .expect("Failed to create DIDComm message");
973 let reply = original_message
974 .create_reply(&settle, creator_did)
975 .expect("Failed to create reply");
976 #crate_path::message::tap_message_trait::typed_plain_message(reply, settle)
977 }
978
979 fn revert(
980 &self,
981 creator_did: &str,
982 settlement_address: &str,
983 reason: &str,
984 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Revert> {
985 let revert = #crate_path::message::Revert {
986 transaction_id: (#tx_id_access).to_string(),
987 settlement_address: settlement_address.to_string(),
988 reason: reason.to_string(),
989 };
990 let original_message = self
991 .to_didcomm(creator_did)
992 .expect("Failed to create DIDComm message");
993 let reply = original_message
994 .create_reply(&revert, creator_did)
995 .expect("Failed to create reply");
996 #crate_path::message::tap_message_trait::typed_plain_message(reply, revert)
997 }
998
999 fn add_agents(&self, creator_did: &str, agents: Vec<#crate_path::message::Agent>) -> #crate_path::didcomm::PlainMessage<#crate_path::message::AddAgents> {
1000 let add_agents = #crate_path::message::AddAgents {
1001 transaction_id: (#tx_id_access).to_string(),
1002 agents,
1003 };
1004 let original_message = self
1005 .to_didcomm(creator_did)
1006 .expect("Failed to create DIDComm message");
1007 let reply = original_message
1008 .create_reply(&add_agents, creator_did)
1009 .expect("Failed to create reply");
1010 #crate_path::message::tap_message_trait::typed_plain_message(reply, add_agents)
1011 }
1012
1013 fn replace_agent(
1014 &self,
1015 creator_did: &str,
1016 original_agent: &str,
1017 replacement: #crate_path::message::Agent,
1018 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::ReplaceAgent> {
1019 let replace_agent = #crate_path::message::ReplaceAgent {
1020 transaction_id: (#tx_id_access).to_string(),
1021 original: original_agent.to_string(),
1022 replacement,
1023 };
1024 let original_message = self
1025 .to_didcomm(creator_did)
1026 .expect("Failed to create DIDComm message");
1027 let reply = original_message
1028 .create_reply(&replace_agent, creator_did)
1029 .expect("Failed to create reply");
1030 #crate_path::message::tap_message_trait::typed_plain_message(reply, replace_agent)
1031 }
1032
1033 fn remove_agent(&self, creator_did: &str, agent: &str) -> #crate_path::didcomm::PlainMessage<#crate_path::message::RemoveAgent> {
1034 let remove_agent = #crate_path::message::RemoveAgent {
1035 transaction_id: (#tx_id_access).to_string(),
1036 agent: agent.to_string(),
1037 };
1038 let original_message = self
1039 .to_didcomm(creator_did)
1040 .expect("Failed to create DIDComm message");
1041 let reply = original_message
1042 .create_reply(&remove_agent, creator_did)
1043 .expect("Failed to create reply");
1044 #crate_path::message::tap_message_trait::typed_plain_message(reply, remove_agent)
1045 }
1046
1047 fn update_party(
1048 &self,
1049 creator_did: &str,
1050 party_type: &str,
1051 party: #crate_path::message::Party,
1052 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::UpdateParty> {
1053 let update_party = #crate_path::message::UpdateParty {
1054 transaction_id: (#tx_id_access).to_string(),
1055 party_type: party_type.to_string(),
1056 party,
1057 context: None,
1058 };
1059 let original_message = self
1060 .to_didcomm(creator_did)
1061 .expect("Failed to create DIDComm message");
1062 let reply = original_message
1063 .create_reply(&update_party, creator_did)
1064 .expect("Failed to create reply");
1065 #crate_path::message::tap_message_trait::typed_plain_message(reply, update_party)
1066 }
1067
1068 fn update_policies(
1069 &self,
1070 creator_did: &str,
1071 policies: Vec<#crate_path::message::Policy>,
1072 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::UpdatePolicies> {
1073 let update_policies = #crate_path::message::UpdatePolicies {
1074 transaction_id: (#tx_id_access).to_string(),
1075 policies,
1076 };
1077 let original_message = self
1078 .to_didcomm(creator_did)
1079 .expect("Failed to create DIDComm message");
1080 let reply = original_message
1081 .create_reply(&update_policies, creator_did)
1082 .expect("Failed to create reply");
1083 #crate_path::message::tap_message_trait::typed_plain_message(reply, update_policies)
1084 }
1085
1086 fn confirm_relationship(
1087 &self,
1088 creator_did: &str,
1089 agent_did: &str,
1090 relationship_type: &str,
1091 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::ConfirmRelationship> {
1092 let confirm_relationship = #crate_path::message::ConfirmRelationship {
1093 transaction_id: (#tx_id_access).to_string(),
1094 agent_id: agent_did.to_string(),
1095 relationship_type: relationship_type.to_string(),
1096 };
1097 let original_message = self
1098 .to_didcomm(creator_did)
1099 .expect("Failed to create DIDComm message");
1100 let reply = original_message
1101 .create_reply(&confirm_relationship, creator_did)
1102 .expect("Failed to create reply");
1103 #crate_path::message::tap_message_trait::typed_plain_message(reply, confirm_relationship)
1104 }
1105 }
1106 }
1107}