plutus_data_derive/
encoding.rs1use 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 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 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