forwarding_gen/
method_member_adapter.rs

1/*
2Hereditary
3Autors: Francisco Leon <https://github.com/superoptimo>
4License Apache-2.0
5*/
6
7use proc_macro2::token_stream::TokenStream as TkStream;
8use quote::ToTokens;
9use syn::{Ident, Signature, Expr};
10use proc_macro2::Span;
11use sha3::{Digest, Sha3_256};
12use base32ct::{Base32Unpadded, Encoding};
13
14#[derive(Debug, Clone)]
15pub(crate) enum MethodAdaptError
16{
17    NotDispatchable(Span),
18    MalformedArgument(Span),
19    SyntaxError(syn::Error)
20}
21
22impl From<MethodAdaptError> for syn::Error
23{
24    fn from(value: MethodAdaptError) -> Self {
25        match value {
26            MethodAdaptError::NotDispatchable(s) => syn::Error::new(s, "Method not dispatchable. Object-Safe Traits require a valid receiver."),
27            MethodAdaptError::MalformedArgument(s) => syn::Error::new(s, "Malformed argument in Signature, identifier required."),
28            MethodAdaptError::SyntaxError(err) => err
29        }
30    }
31}
32
33impl From<syn::Error> for MethodAdaptError
34{
35    fn from(value: syn::Error) -> Self {
36        MethodAdaptError::SyntaxError(value)
37    }
38}
39
40impl std::fmt::Display for MethodAdaptError
41{
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        
43        write!(f,"{}", syn::Error::from(self.clone()))
44    }
45}
46
47fn generate_macro_signature_hash(
48    submember:&Ident, 
49    method_sig:&Signature,
50    base_hash:&str
51) -> String
52{
53    let strbulk = format!("member:{},signature:{},{}", 
54        submember.to_string(),
55        method_sig.to_token_stream(),
56        base_hash
57    );
58
59    let mut hasher = Sha3_256::new();
60    hasher.update(strbulk.as_bytes());
61    let finv = hasher.finalize();
62    Base32Unpadded::encode_string(&finv[..])
63}
64
65pub(crate) struct TraitMethodImplMacros
66{
67    pub(crate) macro_decl:TkStream,
68    pub(crate) macro_invoke:TkStream
69}
70
71impl TraitMethodImplMacros
72{
73    pub(crate) fn create(
74        struct_member: &Ident, 
75        method_sig: &Signature,
76        base_hash:&str
77    ) -> Result<Self, MethodAdaptError>
78    {
79
80        let mut params_iter = method_sig.inputs.iter();
81
82        // obtain first parameter
83        let first_param = params_iter.next().ok_or(
84            MethodAdaptError::NotDispatchable(method_sig.ident.span())
85        )?;
86
87        // check if first parameter is a valid receiver
88        let receiver_pat:TkStream = match first_param {
89            syn::FnArg::Receiver(rcv) => {
90                if rcv.reference.is_some() {
91                    let muttk = rcv.mutability;
92                    let stks = quote::quote!(&#muttk);
93                    Ok(stks)
94                }
95                else
96                {
97                    Err(MethodAdaptError::NotDispatchable(method_sig.ident.span()))
98                }                
99            },
100            _ => Err(MethodAdaptError::NotDispatchable(method_sig.ident.span()))
101        }?;
102
103        
104        // extract the rest of parameters names
105        let parameters_pair: Vec<(Expr, syn::FnArg)> = params_iter.map(
106            |arg| -> Result<(Expr, syn::FnArg), MethodAdaptError> {
107                match arg {
108                    syn::FnArg::Receiver(_) => {
109                        // Method needs to be dispatchable
110                        Err(MethodAdaptError::NotDispatchable(method_sig.ident.span()))
111                    },
112                    syn::FnArg::Typed(typed) => {
113                        let innerpat = typed.pat.as_ref();
114                        match innerpat {
115                            syn::Pat::Ident(idx) => {
116                                let ret_expr= syn::parse2::<Expr>(idx.ident.to_token_stream());
117                                ret_expr.map(|expr | (expr, arg.clone() ) ).or_else(|err| Err(err.into()))                            
118                            },
119                            _ => {
120                                Err(MethodAdaptError::MalformedArgument(method_sig.ident.span()))
121                            }
122                        }
123                    }
124                }
125            }
126        ).collect::<Result< _ , _> >()?;
127
128        let (parameters, signature_params) : (Vec<Expr>, Vec<syn::FnArg>) = parameters_pair.into_iter().unzip();
129
130        let method_name = &method_sig.ident;
131        
132        // If it doesn't have a return type, put a semi colon
133        let method_output = method_sig.output.clone();
134        let semi_end:Option<syn::token::Semi>  = match &method_output {
135            syn::ReturnType::Default => {Some(syn::token::Semi::default())},
136            _ => { None }
137        };
138
139        let method_generics = &method_sig.generics;
140        let method_where = &method_sig.generics.where_clause;
141
142
143        // calculate macro name with signature hash
144        let inner_macro_method_name_str = format!("macromethod_{}_{}",
145            method_name.to_string(),
146            generate_macro_signature_hash(struct_member, method_sig, base_hash)
147        );
148
149        let inner_macro_method_name = Ident::new(&inner_macro_method_name_str, method_name.span());
150
151        // macro declaration should be inserted outside trait implementation
152        let mdecl = quote::quote!(
153            macro_rules! #inner_macro_method_name {
154                ($self_token:ident) => {
155                    
156                    fn #method_name #method_generics (#receiver_pat $self_token #(,#signature_params)* ) #method_output
157                    #method_where
158                    {
159                        $self_token.#struct_member.#method_name( #(#parameters),* ) #semi_end
160                    }
161                };
162            }
163        );
164
165        // macro invokation should be inserted inside trait implementation
166        let minvoke = quote::quote!(#inner_macro_method_name!(self););
167
168        Ok(Self{macro_decl:mdecl, macro_invoke:minvoke})
169
170    }
171
172}