1#![no_std]
2
3extern crate alloc;
4extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
9
10#[proc_macro_derive(ValidateCurrencies)]
14pub fn derive_validate_currencies(input: TokenStream) -> TokenStream {
15 let input = parse_macro_input!(input as DeriveInput);
16 let name = input.ident;
17
18 let fields = match input.data {
19 Data::Struct(data_struct) => match data_struct.fields {
20 Fields::Named(fields_named) => fields_named.named,
21 _ => {
22 return syn::Error::new_spanned(name, "Only named fields supported")
23 .to_compile_error()
24 .into();
25 }
26 },
27 _ => {
28 return syn::Error::new_spanned(name, "Only structs are supported")
29 .to_compile_error()
30 .into();
31 }
32 };
33
34 let amount_field_validations = fields.iter().filter_map(|field| {
35 let ident = &field.ident;
36 match &field.ty {
37 Type::Path(type_path) => {
39 use alloc::string::ToString;
40 let segments = &type_path.path.segments;
41 if segments.len() == 1 && segments[0].ident == "Option" {
42 if let syn::PathArguments::AngleBracketed(angle_bracketed) =
44 &segments[0].arguments
45 {
46 if let Some(syn::GenericArgument::Type(Type::Path(inner_type_path))) =
47 angle_bracketed.args.first()
48 {
49 let inner_ident = &inner_type_path.path.segments.last().unwrap().ident;
50 if [
51 "Amount",
52 "XRPAmount",
53 "IssuedCurrencyAmount",
54 "Currency",
55 "XRP",
56 "IssuedCurrency",
57 ]
58 .contains(&inner_ident.to_string().as_str())
59 {
60 return Some(quote! {
61 if let Some(x) = &self.#ident {
62 x.validate()?;
63 }
64 });
65 }
66 }
67 }
68 }
69
70 let type_ident = &segments.last().unwrap().ident;
72 if [
73 "Amount",
74 "XRPAmount",
75 "IssuedCurrencyAmount",
76 "Currency",
77 "XRP",
78 "IssuedCurrency",
79 ]
80 .contains(&type_ident.to_string().as_str())
81 {
82 return Some(quote! {
83 self.#ident.validate()?;
84 });
85 }
86
87 None
88 }
89 _ => None,
90 }
91 });
92
93 let generics = input.generics;
94 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
95
96 let expanded = quote! {
97 impl #impl_generics ValidateCurrencies for #name #ty_generics #where_clause {
98 fn validate_currencies(&self) -> crate::models::XRPLModelResult<()> {
99 #(#amount_field_validations)*
100
101 Ok(())
102 }
103 }
104 };
105
106 TokenStream::from(expanded)
107}