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 if is_optional_type(&field.ty) {
343 field_info.optional_transaction_id_field = Some(field_name.clone());
344 } else {
345 field_info.transaction_id_field = Some(field_name.clone());
346 }
347 } else if meta.path.is_ident("optional_transaction_id") {
348 field_info.optional_transaction_id_field = Some(field_name.clone());
349 } else if meta.path.is_ident("thread_id") {
350 if is_optional_type(&field.ty) {
352 field_info.optional_thread_id_field = Some(field_name.clone());
353 } else {
354 field_info.thread_id_field = Some(field_name.clone());
355 }
356 } else if meta.path.is_ident("connection_id") {
357 field_info.connection_id_field = Some(field_name.clone());
358 } else if meta.path.is_ident("generated_id") {
359 field_info.has_generated_id = true;
360 }
361 Ok(())
362 });
363 }
364 }
365 }
366
367 field_info
368}
369
370fn is_optional_type(ty: &syn::Type) -> bool {
371 if let syn::Type::Path(type_path) = ty {
372 if let Some(segment) = type_path.path.segments.last() {
373 return segment.ident == "Option";
374 }
375 }
376 false
377}
378
379fn impl_tap_message_trait(
380 name: &syn::Ident,
381 field_info: &FieldInfo,
382 impl_generics: &syn::ImplGenerics,
383 ty_generics: &syn::TypeGenerics,
384 where_clause: Option<&syn::WhereClause>,
385 is_internal: bool,
386) -> TokenStream2 {
387 let thread_id_impl = generate_thread_id_impl(field_info);
388 let message_id_impl = generate_message_id_impl(field_info);
389 let get_all_participants_impl = generate_get_all_participants_impl(field_info);
390
391 let crate_path = if is_internal {
392 quote! { crate }
393 } else {
394 quote! { ::tap_msg }
395 };
396
397 quote! {
400 impl #impl_generics #crate_path::message::tap_message_trait::TapMessage for #name #ty_generics #where_clause {
401 fn validate(&self) -> #crate_path::error::Result<()> {
402 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::validate(self)
403 }
404
405 fn is_tap_message(&self) -> bool {
406 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type()
407 .starts_with("https://tap.rsvp/schema/1.0#")
408 }
409
410 fn get_tap_type(&self) -> Option<String> {
411 Some(
412 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type()
413 .to_string(),
414 )
415 }
416
417 fn body_as<T: #crate_path::message::tap_message_trait::TapMessageBody>(
418 &self,
419 ) -> #crate_path::error::Result<T> {
420 unimplemented!()
421 }
422
423 fn get_all_participants(&self) -> Vec<String> {
424 use #crate_path::message::agent::TapParticipant;
426 #get_all_participants_impl
427 }
428
429 fn create_reply<T: #crate_path::message::tap_message_trait::TapMessageBody>(
430 &self,
431 body: &T,
432 creator_did: &str,
433 ) -> #crate_path::error::Result<#crate_path::didcomm::PlainMessage> {
434 let mut message = body.to_didcomm(creator_did)?;
436
437 if let Some(thread_id) = self.thread_id() {
441 message.thid = Some(thread_id.to_string());
442 } else {
443 message.thid = Some(self.message_id().to_string());
445 }
446
447 if let Some(parent_thread_id) = self.parent_thread_id() {
449 message.pthid = Some(parent_thread_id.to_string());
450 }
451
452 Ok(message)
453 }
454
455
456 fn thread_id(&self) -> Option<&str> {
457 #thread_id_impl
458 }
459
460 fn parent_thread_id(&self) -> Option<&str> {
461 None
462 }
463
464 fn message_id(&self) -> &str {
465 #message_id_impl
466 }
467 }
468 }
469}
470
471fn impl_message_context_trait(
472 name: &syn::Ident,
473 field_info: &FieldInfo,
474 impl_generics: &syn::ImplGenerics,
475 ty_generics: &syn::TypeGenerics,
476 where_clause: Option<&syn::WhereClause>,
477 is_internal: bool,
478) -> TokenStream2 {
479 let participant_dids_impl = generate_participant_dids_impl(field_info);
480 let transaction_context_impl = generate_transaction_context_impl(field_info, is_internal);
481
482 let crate_path = if is_internal {
483 quote! { crate }
484 } else {
485 quote! { ::tap_msg }
486 };
487
488 quote! {
489 impl #impl_generics #crate_path::message::MessageContext for #name #ty_generics #where_clause {
490 fn participant_dids(&self) -> Vec<String> {
491 use #crate_path::message::agent::TapParticipant;
493 #participant_dids_impl
494 }
495
496 fn transaction_context(&self) -> Option<#crate_path::message::TransactionContext> {
497 #transaction_context_impl
498 }
499 }
500 }
501}
502
503fn generate_participant_dids_impl(field_info: &FieldInfo) -> TokenStream2 {
504 let mut did_extracts = Vec::new();
505
506 for field in &field_info.participant_fields {
508 did_extracts.push(quote! {
509 dids.push(self.#field.id().to_string());
511 });
512 }
513
514 for field in &field_info.optional_participant_fields {
516 did_extracts.push(quote! {
517 if let Some(ref participant) = self.#field {
518 dids.push(participant.id().to_string());
519 }
520 });
521 }
522
523 for field in &field_info.participant_list_fields {
525 did_extracts.push(quote! {
526 for participant in &self.#field {
527 dids.push(participant.id().to_string());
528 }
529 });
530 }
531
532 quote! {
533 let mut dids = Vec::new();
534 #(#did_extracts)*
535 dids
536 }
537}
538
539fn generate_get_all_participants_impl(field_info: &FieldInfo) -> TokenStream2 {
540 let mut participant_extracts = Vec::new();
541
542 for field in &field_info.participant_fields {
544 participant_extracts.push(quote! {
545 participants.push(self.#field.id().to_string());
546 });
547 }
548
549 for field in &field_info.optional_participant_fields {
551 participant_extracts.push(quote! {
552 if let Some(ref participant) = self.#field {
553 participants.push(participant.id().to_string());
554 }
555 });
556 }
557
558 for field in &field_info.participant_list_fields {
560 participant_extracts.push(quote! {
561 for participant in &self.#field {
562 participants.push(participant.id().to_string());
563 }
564 });
565 }
566
567 quote! {
568 let mut participants = Vec::new();
569 #(#participant_extracts)*
570 participants
571 }
572}
573
574fn generate_thread_id_impl(field_info: &FieldInfo) -> TokenStream2 {
575 if let Some(ref thread_field) = field_info.thread_id_field {
576 quote! { Some(&self.#thread_field) }
577 } else if let Some(ref opt_thread_field) = field_info.optional_thread_id_field {
578 quote! { self.#opt_thread_field.as_deref() }
579 } else if field_info.is_initiator {
580 quote! { None }
582 } else if let Some(ref tx_field) = field_info.transaction_id_field {
583 quote! { Some(&self.#tx_field) }
584 } else if let Some(ref opt_tx_field) = field_info.optional_transaction_id_field {
585 quote! { self.#opt_tx_field.as_deref() }
586 } else {
587 quote! { None }
588 }
589}
590
591fn generate_message_id_impl(field_info: &FieldInfo) -> TokenStream2 {
592 if let Some(tx_field) = &field_info.transaction_id_field {
593 quote! { &self.#tx_field }
594 } else if let Some(thread_field) = &field_info.thread_id_field {
595 quote! { &self.#thread_field }
596 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
597 quote! {
598 self.#opt_tx_field.as_deref().unwrap_or("")
599 }
600 } else {
601 quote! {
602 static FALLBACK_ID: &str = "00000000-0000-0000-0000-000000000000";
605 FALLBACK_ID
606 }
607 }
608}
609
610fn generate_transaction_context_impl(field_info: &FieldInfo, is_internal: bool) -> TokenStream2 {
611 let crate_path = if is_internal {
612 quote! { crate }
613 } else {
614 quote! { ::tap_msg }
615 };
616
617 if let Some(tx_field) = &field_info.transaction_id_field {
618 quote! {
619 Some(#crate_path::message::TransactionContext::new(
620 self.#tx_field.clone(),
621 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type().to_string(),
622 ))
623 }
624 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
625 quote! {
626 self.#opt_tx_field.as_ref().map(|tx_id| {
627 #crate_path::message::TransactionContext::new(
628 tx_id.clone(),
629 <Self as #crate_path::message::tap_message_trait::TapMessageBody>::message_type().to_string(),
630 )
631 })
632 }
633 } else {
634 quote! { None }
635 }
636}
637
638fn impl_tap_message_body_trait(
639 name: &syn::Ident,
640 field_info: &FieldInfo,
641 impl_generics: &syn::ImplGenerics,
642 ty_generics: &syn::TypeGenerics,
643 where_clause: Option<&syn::WhereClause>,
644 is_internal: bool,
645) -> TokenStream2 {
646 let crate_path = if is_internal {
647 quote! { crate }
648 } else {
649 quote! { ::tap_msg }
650 };
651
652 let message_type = field_info
653 .message_type
654 .as_ref()
655 .expect("message_type attribute is required for TapMessageBody");
656
657 let validate_impl = if field_info.custom_validation {
658 let method_name = syn::Ident::new(
660 &format!("validate_{}", name.to_string().to_lowercase()),
661 name.span(),
662 );
663 quote! {
664 self.#method_name()
665 }
666 } else {
667 quote! {
668 Ok(())
670 }
671 };
672
673 let to_didcomm_impl = generate_to_didcomm_impl(field_info, is_internal);
674
675 quote! {
676 impl #impl_generics #crate_path::message::tap_message_trait::TapMessageBody for #name #ty_generics #where_clause {
677 fn message_type() -> &'static str {
678 #message_type
679 }
680
681 fn validate(&self) -> #crate_path::error::Result<()> {
682 #validate_impl
683 }
684
685 fn to_didcomm(&self, from_did: &str) -> #crate_path::error::Result<#crate_path::didcomm::PlainMessage> {
686 #to_didcomm_impl
687 }
688 }
689 }
690}
691
692fn generate_to_didcomm_impl(field_info: &FieldInfo, is_internal: bool) -> TokenStream2 {
693 let crate_path = if is_internal {
694 quote! { crate }
695 } else {
696 quote! { ::tap_msg }
697 };
698
699 let participant_extraction = if !field_info.participant_fields.is_empty()
701 || !field_info.optional_participant_fields.is_empty()
702 || !field_info.participant_list_fields.is_empty()
703 {
704 let mut extracts = Vec::new();
705
706 for field in &field_info.participant_fields {
708 extracts.push(quote! {
709 recipient_dids.push(self.#field.id().to_string());
710 });
711 }
712
713 for field in &field_info.optional_participant_fields {
715 extracts.push(quote! {
716 if let Some(ref participant) = self.#field {
717 recipient_dids.push(participant.id().to_string());
718 }
719 });
720 }
721
722 for field in &field_info.participant_list_fields {
724 extracts.push(quote! {
725 for participant in &self.#field {
726 recipient_dids.push(participant.id().to_string());
727 }
728 });
729 }
730
731 quote! {
732 let mut recipient_dids = Vec::new();
733 #(#extracts)*
734
735 recipient_dids.sort();
737 recipient_dids.dedup();
738 recipient_dids.retain(|did| did != from_did);
739 }
740 } else {
741 quote! {
742 let recipient_dids: Vec<String> = Vec::new();
743 }
744 };
745
746 let thread_assignment = if field_info.is_initiator {
748 if let Some(tx_field) = &field_info.transaction_id_field {
750 quote! {
751 thid: Some(self.#tx_field.clone()),
752 }
753 } else {
754 quote! {
755 thid: None,
756 }
757 }
758 } else if let Some(thread_field) = &field_info.thread_id_field {
759 quote! {
761 thid: Some(self.#thread_field.clone()),
762 }
763 } else if let Some(opt_thread_field) = &field_info.optional_thread_id_field {
764 quote! {
766 thid: self.#opt_thread_field.clone(),
767 }
768 } else if let Some(tx_field) = &field_info.transaction_id_field {
769 quote! {
771 thid: Some(self.#tx_field.clone()),
772 }
773 } else if let Some(opt_tx_field) = &field_info.optional_transaction_id_field {
774 quote! {
775 thid: self.#opt_tx_field.clone(),
776 }
777 } else {
778 quote! {
779 thid: None,
780 }
781 };
782
783 let pthid_assignment = if let Some(conn_field) = &field_info.connection_id_field {
785 quote! {
786 pthid: self.#conn_field.clone(),
787 }
788 } else {
789 quote! {
790 pthid: None,
791 }
792 };
793
794 quote! {
795 use #crate_path::message::agent::TapParticipant;
797
798 let mut body_json = serde_json::to_value(self)
800 .map_err(|e| #crate_path::error::Error::SerializationError(e.to_string()))?;
801
802 if let Some(body_obj) = body_json.as_object_mut() {
804 body_obj.insert(
805 "@type".to_string(),
806 serde_json::Value::String(Self::message_type().to_string()),
807 );
808 }
809
810 #participant_extraction
812
813 let now = chrono::Utc::now().timestamp() as u64;
814
815 Ok(#crate_path::didcomm::PlainMessage {
817 id: uuid::Uuid::new_v4().to_string(),
818 typ: "application/didcomm-plain+json".to_string(),
819 type_: Self::message_type().to_string(),
820 body: body_json,
821 from: from_did.to_string(),
822 to: recipient_dids,
823 #thread_assignment
824 #pthid_assignment
825 created_time: Some(now),
826 expires_time: None,
827 extra_headers: std::collections::HashMap::new(),
828 from_prior: None,
829 attachments: None,
830 })
831 }
832}
833
834fn impl_tap_message_body_only(input: &DeriveInput) -> TokenStream2 {
835 let name = &input.ident;
836 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
837
838 let fields = match &input.data {
839 Data::Struct(data) => match &data.fields {
840 Fields::Named(fields) => &fields.named,
841 _ => panic!("TapMessageBody can only be derived for structs with named fields"),
842 },
843 _ => panic!("TapMessageBody can only be derived for structs"),
844 };
845
846 let field_info = analyze_fields(fields, &input.attrs);
847
848 let is_internal = std::env::var("CARGO_CRATE_NAME").unwrap_or_default() == "tap_msg";
850
851 if field_info.message_type.is_none() {
853 panic!("TapMessageBody derive macro requires #[tap(message_type = \"...\")] attribute");
854 }
855
856 impl_tap_message_body_trait(
857 name,
858 &field_info,
859 &impl_generics,
860 &ty_generics,
861 where_clause,
862 is_internal,
863 )
864}
865
866fn impl_authorizable_trait(
867 name: &syn::Ident,
868 field_info: &FieldInfo,
869 impl_generics: &syn::ImplGenerics,
870 ty_generics: &syn::TypeGenerics,
871 where_clause: Option<&syn::WhereClause>,
872 is_internal: bool,
873) -> TokenStream2 {
874 let crate_path = if is_internal {
875 quote! { crate }
876 } else {
877 quote! { ::tap_msg }
878 };
879
880 let tx_id_access = if let Some(ref tx_field) = field_info.transaction_id_field {
882 quote! { &self.#tx_field }
883 } else if let Some(ref opt_tx_field) = field_info.optional_transaction_id_field {
884 quote! { self.#opt_tx_field.as_deref().unwrap_or("") }
885 } else if let Some(ref thread_field) = field_info.thread_id_field {
886 quote! { &self.#thread_field }
887 } else {
888 quote! { "" }
889 };
890
891 quote! {
892 impl #impl_generics #crate_path::message::tap_message_trait::Authorizable for #name #ty_generics #where_clause {
893 fn authorize(
894 &self,
895 creator_did: &str,
896 settlement_address: Option<&str>,
897 expiry: Option<&str>,
898 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Authorize> {
899 let authorize = #crate_path::message::Authorize::with_all(#tx_id_access, settlement_address, expiry);
900 let original_message = self
901 .to_didcomm(creator_did)
902 .expect("Failed to create DIDComm message");
903 let reply = original_message
904 .create_reply(&authorize, creator_did)
905 .expect("Failed to create reply");
906 #crate_path::message::tap_message_trait::typed_plain_message(reply, authorize)
907 }
908
909 fn cancel(&self, creator_did: &str, by: &str, reason: Option<&str>) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Cancel> {
910 let cancel = if let Some(reason) = reason {
911 #crate_path::message::Cancel::with_reason(#tx_id_access, by, reason)
912 } else {
913 #crate_path::message::Cancel::new(#tx_id_access, by)
914 };
915 let original_message = self
916 .to_didcomm(creator_did)
917 .expect("Failed to create DIDComm message");
918 let reply = original_message
919 .create_reply(&cancel, creator_did)
920 .expect("Failed to create reply");
921 #crate_path::message::tap_message_trait::typed_plain_message(reply, cancel)
922 }
923
924 fn reject(&self, creator_did: &str, reason: &str) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Reject> {
925 let reject = #crate_path::message::Reject {
926 transaction_id: (#tx_id_access).to_string(),
927 reason: Some(reason.to_string()),
928 };
929 let original_message = self
930 .to_didcomm(creator_did)
931 .expect("Failed to create DIDComm message");
932 let reply = original_message
933 .create_reply(&reject, creator_did)
934 .expect("Failed to create reply");
935 #crate_path::message::tap_message_trait::typed_plain_message(reply, reject)
936 }
937 }
938 }
939}
940
941fn impl_transaction_trait(
942 name: &syn::Ident,
943 field_info: &FieldInfo,
944 impl_generics: &syn::ImplGenerics,
945 ty_generics: &syn::TypeGenerics,
946 where_clause: Option<&syn::WhereClause>,
947 is_internal: bool,
948) -> TokenStream2 {
949 let crate_path = if is_internal {
950 quote! { crate }
951 } else {
952 quote! { ::tap_msg }
953 };
954
955 let tx_id_access = if let Some(ref tx_field) = field_info.transaction_id_field {
957 quote! { &self.#tx_field }
958 } else if let Some(ref opt_tx_field) = field_info.optional_transaction_id_field {
959 quote! { self.#opt_tx_field.as_deref().unwrap_or("") }
960 } else if let Some(ref thread_field) = field_info.thread_id_field {
961 quote! { &self.#thread_field }
962 } else {
963 quote! { "" }
964 };
965
966 quote! {
967 impl #impl_generics #crate_path::message::tap_message_trait::Transaction for #name #ty_generics #where_clause {
968 fn settle(
969 &self,
970 creator_did: &str,
971 settlement_id: &str,
972 amount: Option<&str>,
973 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Settle> {
974 let settle = #crate_path::message::Settle {
975 transaction_id: (#tx_id_access).to_string(),
976 settlement_id: Some(settlement_id.to_string()),
977 amount: amount.map(|s| s.to_string()),
978 };
979 let original_message = self
980 .to_didcomm(creator_did)
981 .expect("Failed to create DIDComm message");
982 let reply = original_message
983 .create_reply(&settle, creator_did)
984 .expect("Failed to create reply");
985 #crate_path::message::tap_message_trait::typed_plain_message(reply, settle)
986 }
987
988 fn revert(
989 &self,
990 creator_did: &str,
991 settlement_address: &str,
992 reason: &str,
993 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::Revert> {
994 let revert = #crate_path::message::Revert {
995 transaction_id: (#tx_id_access).to_string(),
996 settlement_address: settlement_address.to_string(),
997 reason: reason.to_string(),
998 };
999 let original_message = self
1000 .to_didcomm(creator_did)
1001 .expect("Failed to create DIDComm message");
1002 let reply = original_message
1003 .create_reply(&revert, creator_did)
1004 .expect("Failed to create reply");
1005 #crate_path::message::tap_message_trait::typed_plain_message(reply, revert)
1006 }
1007
1008 fn add_agents(&self, creator_did: &str, agents: Vec<#crate_path::message::Agent>) -> #crate_path::didcomm::PlainMessage<#crate_path::message::AddAgents> {
1009 let add_agents = #crate_path::message::AddAgents {
1010 transaction_id: (#tx_id_access).to_string(),
1011 agents,
1012 };
1013 let original_message = self
1014 .to_didcomm(creator_did)
1015 .expect("Failed to create DIDComm message");
1016 let reply = original_message
1017 .create_reply(&add_agents, creator_did)
1018 .expect("Failed to create reply");
1019 #crate_path::message::tap_message_trait::typed_plain_message(reply, add_agents)
1020 }
1021
1022 fn replace_agent(
1023 &self,
1024 creator_did: &str,
1025 original_agent: &str,
1026 replacement: #crate_path::message::Agent,
1027 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::ReplaceAgent> {
1028 let replace_agent = #crate_path::message::ReplaceAgent {
1029 transaction_id: (#tx_id_access).to_string(),
1030 original: original_agent.to_string(),
1031 replacement,
1032 };
1033 let original_message = self
1034 .to_didcomm(creator_did)
1035 .expect("Failed to create DIDComm message");
1036 let reply = original_message
1037 .create_reply(&replace_agent, creator_did)
1038 .expect("Failed to create reply");
1039 #crate_path::message::tap_message_trait::typed_plain_message(reply, replace_agent)
1040 }
1041
1042 fn remove_agent(&self, creator_did: &str, agent: &str) -> #crate_path::didcomm::PlainMessage<#crate_path::message::RemoveAgent> {
1043 let remove_agent = #crate_path::message::RemoveAgent {
1044 transaction_id: (#tx_id_access).to_string(),
1045 agent: agent.to_string(),
1046 };
1047 let original_message = self
1048 .to_didcomm(creator_did)
1049 .expect("Failed to create DIDComm message");
1050 let reply = original_message
1051 .create_reply(&remove_agent, creator_did)
1052 .expect("Failed to create reply");
1053 #crate_path::message::tap_message_trait::typed_plain_message(reply, remove_agent)
1054 }
1055
1056 fn update_party(
1057 &self,
1058 creator_did: &str,
1059 party_type: &str,
1060 party: #crate_path::message::Party,
1061 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::UpdateParty> {
1062 let update_party = #crate_path::message::UpdateParty {
1063 transaction_id: (#tx_id_access).to_string(),
1064 party_type: party_type.to_string(),
1065 party,
1066 context: None,
1067 };
1068 let original_message = self
1069 .to_didcomm(creator_did)
1070 .expect("Failed to create DIDComm message");
1071 let reply = original_message
1072 .create_reply(&update_party, creator_did)
1073 .expect("Failed to create reply");
1074 #crate_path::message::tap_message_trait::typed_plain_message(reply, update_party)
1075 }
1076
1077 fn update_policies(
1078 &self,
1079 creator_did: &str,
1080 policies: Vec<#crate_path::message::Policy>,
1081 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::UpdatePolicies> {
1082 let update_policies = #crate_path::message::UpdatePolicies {
1083 transaction_id: (#tx_id_access).to_string(),
1084 policies,
1085 };
1086 let original_message = self
1087 .to_didcomm(creator_did)
1088 .expect("Failed to create DIDComm message");
1089 let reply = original_message
1090 .create_reply(&update_policies, creator_did)
1091 .expect("Failed to create reply");
1092 #crate_path::message::tap_message_trait::typed_plain_message(reply, update_policies)
1093 }
1094
1095 fn confirm_relationship(
1096 &self,
1097 creator_did: &str,
1098 agent_did: &str,
1099 for_entity: &str,
1100 ) -> #crate_path::didcomm::PlainMessage<#crate_path::message::ConfirmRelationship> {
1101 let confirm_relationship = #crate_path::message::ConfirmRelationship {
1102 transaction_id: (#tx_id_access).to_string(),
1103 agent_id: agent_did.to_string(),
1104 for_entity: for_entity.to_string(),
1105 role: None,
1106 };
1107 let original_message = self
1108 .to_didcomm(creator_did)
1109 .expect("Failed to create DIDComm message");
1110 let reply = original_message
1111 .create_reply(&confirm_relationship, creator_did)
1112 .expect("Failed to create reply");
1113 #crate_path::message::tap_message_trait::typed_plain_message(reply, confirm_relationship)
1114 }
1115 }
1116 }
1117}