plutus_data_derive/
encoding.rs

1use super::*;
2
3pub (crate) fn encode_field_value(field:&syn::Field,selfie:bool,attribs:&[String]) -> syn::__private::TokenStream2 {
4
5    let ident = 
6        match &field.ident {
7            None => return quote!{Err(String::from("An unknown error occurred while attempting to encode a field value."))},
8            Some(x) => x
9        };
10    
11    let ident_ref = if selfie { 
12        quote! { self.#ident }
13    } else {
14        quote! { #ident }
15    };
16
17    quote! {
18        let attributes : Vec<String> = vec![#(String::from(#attribs)),*];
19        #ident_ref.to_plutus_data(&attributes)
20    }
21}
22
23
24
25pub (crate) fn handle_struct_encoding(
26    mut fields:syn::punctuated::Punctuated<syn::Field,syn::token::Comma>,
27    name:syn::Ident,attributes:Vec<syn::Attribute>
28) -> proc_macro::TokenStream {
29    let attributes = attributes.into_iter().map(|a| a.path.get_ident().unwrap().to_string() )
30                            .collect::<Vec<String>>();
31    let alphabet = 
32        (b'a'..=b'z')
33        .map(|c| c as char)
34        .filter(|c| c.is_alphabetic())
35        .collect::<Vec<_>>();
36    
37    let mut field_idents = vec![];
38    let mut encoder_field_handlers = vec![];
39    let mut field_iter = fields.iter_mut();
40    
41    for ii in 0..field_iter.len() {
42
43        let field = field_iter.next().unwrap();
44        let my_field = match &field.ident {
45            Some(_f) => field,
46            None => {
47                let mut field_ident = quote::format_ident!(
48                    "{}", alphabet[ii].to_string()
49                );
50                field_ident.set_span(name.span());
51                field_idents.push(field_ident.clone());
52                field.ident = Some(field_ident);
53                field
54            },
55        };
56
57        // todo: this is stupid.
58        let mut attribs = 
59            my_field.clone().attrs.into_iter()
60                .map(|a| a.path.get_ident().unwrap().to_string() )
61                .collect::<Vec<String>>();
62
63        for x in &attributes {
64            attribs.push(x.clone())
65        }
66
67        let val_quote = encode_field_value(my_field,field_idents.is_empty(),&attribs);
68        encoder_field_handlers.push(quote!{
69            let vg : Result<plutus_data::PlutusData,String> = {
70                #val_quote
71            };
72            items.push(vg?);
73        });
74    }
75    
76
77    let woop = 
78        if field_idents.is_empty() {
79            quote! { }
80        } else {
81            quote! { let Self (#(#field_idents),*) = self;}
82        };
83    
84    let combo = quote! {
85        use plutus_data::*;
86        impl plutus_data::ToPlutusData for #name {
87            fn to_plutus_data(&self,attribs:&[String]) -> Result<plutus_data::PlutusData,String> {
88                #woop
89                let mut items = vec![];
90                #(#encoder_field_handlers)*
91                Ok(plutus_data::cp::make_constr(0,items))
92            }
93            
94        }
95    };
96    TokenStream::from(combo)
97}
98
99pub (crate) fn data_enum_encoding_handling(v:syn::DataEnum,name:syn::Ident,attributes:Vec<syn::Attribute>) -> proc_macro::TokenStream {
100    
101    let variants = v.variants.into_iter();
102    let mut encoder_variant_handlers = vec![];
103    let attributes = attributes.into_iter().map(|a| a.path.get_ident().unwrap().to_string() )
104                            .collect::<Vec<String>>();
105    let alphabet = 
106        (b'a'..=b'z')
107        .map(|c| c as char)
108        .filter(|c| c.is_alphabetic())
109        .collect::<Vec<_>>();
110
111    let mut constructor_id : i64 = -1;
112
113    for ev in variants {
114
115        constructor_id += 1;
116        let variant_ident = ev.ident.clone();        
117        let field_count = ev.fields.len();
118        let variant_attribs = ev.clone().attrs.into_iter()
119            .map(|a| a.path.get_ident().unwrap().to_string() )
120            .collect::<Vec<String>>();
121        let is_forced = ev.clone().attrs.into_iter().any(|a|format!("{:?}",a.path.get_ident().unwrap().to_string()).contains("force_variant"));
122       
123        if is_forced {
124            crate::info(&variant_ident,&format!("When encoding this type, we will only allow it to be of the specified variant '{}', and it will be packed as the inner data of the variant, ie. it will not be wrapped in constr data.",variant_ident.to_string()));
125        }
126
127        if field_count == 0 {
128
129            if is_forced {
130                encoder_variant_handlers.clear();
131            }
132
133            encoder_variant_handlers.push(quote!{
134                #name::#variant_ident => {
135                    let my_constructor_id = #constructor_id as u64;
136                    Ok(plutus_data::cp::make_constr(my_constructor_id,vec![]))
137                }
138            });
139
140            if is_forced { 
141                let enum_name = name.to_string();
142                let variant_name = variant_ident.to_string();
143                let variant_full_name = format!("{}::{}",enum_name,variant_name);
144                encoder_variant_handlers.push(quote!{
145                    _ => Err(format!("This enum has been marked to only allow a specific variant ({:?}) to be used with plutus encoding.. but the current value is not of that variant.",#variant_full_name))
146                });
147                break 
148            }
149
150            continue;
151        } 
152
153        let mut field_idents = vec![];
154        let mut encoder_field_handlers = vec![];
155        let mut field_iter = ev.fields.iter();
156        let mut named = false;
157
158        for ii in 0..field_count {
159
160            let field = field_iter.next().unwrap();
161            
162            let mut attribs = 
163                field.clone().attrs.into_iter()
164                    .map(|a| a.path.get_ident().unwrap().to_string() )
165                    .collect::<Vec<String>>();
166            
167            for x in &attributes {
168                attribs.push(x.clone());
169            }
170
171            for x in &variant_attribs {
172                attribs.push(x.clone());
173            }
174
175            match &field.clone().ident {
176                Some(ff) => {
177                    named = true;
178                    let ff_name = ff.clone().to_string();                   
179                    let field_ident = 
180                        if ff_name.contains('#') {
181                            syn::Ident::new_raw(
182                                &ff_name.replace("r#",""),
183                                ff.span()
184                            )
185                        } else { 
186                            quote::format_ident!("{}",ff_name) 
187                        };
188
189                    field_idents.push(field_ident.clone());
190                    let named_field_for_an_ident = syn::Field { 
191                        attrs: vec![], 
192                        vis: syn::Visibility::Inherited,
193                        ident: Some(field_ident.clone()), 
194                        colon_token: None, 
195                        ty: field.ty.clone() 
196                    };
197                    
198
199                            
200                    let val_quote = encode_field_value(&named_field_for_an_ident,false,&attribs);
201                    encoder_field_handlers.push(quote!{
202                        let v : Result<plutus_data::PlutusData,String> = {
203                            #val_quote
204                        };
205                        items.push(v?);
206                    }); 
207                },
208
209                None => {
210                    let f_name = alphabet[ii].to_string();
211                    let field_ident = quote::format_ident!("{}",f_name);
212                    let field_ident_field = syn::Field { 
213                        attrs: vec![], 
214                        vis: syn::Visibility::Inherited,
215                        ident: Some(field_ident.clone()), 
216                        colon_token: None, 
217                        ty: field.ty.clone() 
218                    };        
219                    field_idents.push(field_ident.clone());
220                    
221                    let val_quote = encode_field_value(&field_ident_field,false,&attribs);
222                    encoder_field_handlers.push(quote!{
223                        let v : Result<plutus_data::PlutusData,String> = {
224                            #val_quote
225                        };
226                        items.push(v?);
227                    });
228                }
229            }
230        }
231
232        
233        let varfieldrefs = if named {
234            quote! {{ #(#field_idents),* }}
235        } else {
236            quote! {( #(#field_idents),* )}
237        };
238
239        if is_forced {
240
241            encoder_variant_handlers.clear();
242            if field_count == 1 {
243                encoder_variant_handlers.push(quote!{
244                    #name::#variant_ident #varfieldrefs => {
245                        let mut items = vec![];
246                        #(#encoder_field_handlers);*
247                        let result = items[0].clone();
248                        Ok(result)
249                    }
250                });
251            } else {
252                
253                encoder_variant_handlers.push(quote!{
254                    #name::#variant_ident #varfieldrefs => {
255                        let my_constructor_id = #constructor_id as u64;
256                        let mut items = vec![];
257                        #(#encoder_field_handlers);*
258                        Ok(plutus_data::cp::make_constr(my_constructor_id,items))
259                    }
260                });
261            }
262        } else {
263                
264        
265        encoder_variant_handlers.push(quote!{
266                #name::#variant_ident #varfieldrefs => {
267                    //println!("not ignoring container on item: {}",stringify!(#variant_ident));
268                    let my_constructor_id = #constructor_id as u64;
269                    let mut items = vec![];
270                    #(#encoder_field_handlers);*
271                    Ok(plutus_data::cp::make_constr(my_constructor_id,items))
272                }
273            });
274        }
275
276        if is_forced { 
277            let enum_name = name.to_string();
278            let variant_name = variant_ident.to_string();
279            let variant_full_name = format!("{}::{}",enum_name,variant_name);
280            encoder_variant_handlers.push(quote!{
281                _ => Err(format!("This enum has been marked to only allow a specific variant ({:?}) to be used with plutus encoding.",#variant_full_name))
282            });
283            break 
284        }
285        
286    }
287
288    let combo = quote! {
289        impl plutus_data::ToPlutusData for #name {
290            fn to_plutus_data(&self,attribs:&[String]) -> Result<plutus_data::PlutusData,String> {
291                match self {
292                    #(#encoder_variant_handlers),*
293                }
294            }
295        }
296    };
297
298    TokenStream::from(combo)
299}
300
301
302