1#![feature(box_into_inner)]
2
3use std::collections::HashMap;
4use std::collections::BTreeMap;
5
6use hex::ToHex;
7use pallas_primitives::Fragment;
8pub use pallas_primitives::babbage::PlutusData;
9pub use plutus_data_derive::FromPlutusDataDerive;
10pub use plutus_data_derive::ToPlutusDataDerive;
11
12mod custom_plutus;
13
14pub use custom_plutus::CustomPlutus as cp;
15use custom_plutus::*;
16
17pub trait ToPlutusData {
18 fn to_plutus_data(&self,attributes:&[String]) -> Result<PlutusData,String>;
19}
20
21pub use custom_plutus::CustomPlutus as pd;
22
23pub trait FromPlutusData<T> {
24
25 fn from_plutus_data(x:PlutusData,attributes:&[String]) -> Result<T,String>;
26}
27use std::error::Error;
28pub fn from_bytes(x:&[u8]) -> Result<PlutusData, Box<dyn Error>> {
29 PlutusData::decode_fragment(x)
30}
31pub fn from_bytes_specific<T:FromPlutusData<T>>(x:&[u8]) -> Result<T, String> {
32 let pd = PlutusData::decode_fragment(x).map_err(|e|format!("{:?}",e))?;
33 T::from_plutus_data(pd, &vec![]).map_err(|e|format!("{:?}",e))
34}
35pub fn from_bytes_specific_debug<T:FromPlutusData<T>>(x:&[u8]) -> Result<T, String> {
36 let pd = PlutusData::decode_fragment(x).map_err(|e|format!("{:?}",e))?;
37 T::from_plutus_data(pd, &vec!["debug_re_encoding".into()]).map_err(|e|format!("{:?}",e))
38}
39pub fn to_bytes(x:&PlutusData) -> Result<Vec<u8>, Box<dyn Error>> {
40 x.encode_fragment()
41}
42pub fn from_hex<T:FromPlutusData<T>>(x:&str) -> Result<T,String> {
43 let bytes = hex::decode(&x).map_err(|e|format!("{:?}",e))?;
44 let pd = PlutusData::decode_fragment(&bytes).map_err(|e|format!("{:?}",e))?;
45 T::from_plutus_data(pd, &vec![]).map_err(|e|format!("{:?}",e))
46}
47pub fn from_hex_debug<T:FromPlutusData<T>>(x:&str) -> Result<T,String> {
48 let bytes = hex::decode(&x).map_err(|e|format!("{:?}",e))?;
49 let pd = PlutusData::decode_fragment(&bytes).map_err(|e|format!("{:?}",e))?;
50 T::from_plutus_data(pd, &vec!["debug_re_encoding".into()]).map_err(|e|format!("{:?}",e))
51}
52pub fn to_hex(x:&PlutusData) -> Result<String, Box<dyn Error>> {
53 let xx = x.encode_fragment()?;
54 let hexed : String = xx.encode_hex();
55 Ok(hexed)
56}
57pub fn encode<T : ToPlutusData>(x:&T) -> Result<PlutusData, String> {
58 x.to_plutus_data(&[])
59}
60pub fn encode_vec<T : ToPlutusData + Clone + std::fmt::Debug>(x:&Vec<T>) -> Result<PlutusData, String> {
61 x.to_plutus_data(&[])
62}
63
64impl<T1,T2> FromPlutusData<(T1,T2)> for (T1,T2) where T1: FromPlutusData<T1>, T2: FromPlutusData<T2> {
65 fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<(T1,T2),String> {
66 match x {
67 PlutusData::Constr(p) => {
68
69 if p.fields.len() != 2 {
70 return Err(format!("expected tuple (list) with two items.. found {:?} with {} items.",p.tag,p.fields.len()))
71 }
72
73 let key_a_plutus_item = p.fields[0].clone();
74 let key_b_plutus_item = p.fields[1].clone();
75 Ok((T1::from_plutus_data(key_a_plutus_item,attribs)?,
76 T2::from_plutus_data(key_b_plutus_item,attribs)?
77 ))
78 },
79 PlutusData::Array(p) => {
80 if p.len() == 2 {
81 let key_a_plutus_item = p[0].clone();
82 let key_b_plutus_item = p[1].clone();
83 Ok((T1::from_plutus_data(key_a_plutus_item,attribs)?,
84 T2::from_plutus_data(key_b_plutus_item,attribs)?
85 ))
86 } else {
87 Err(format!("invalid length for tuple data {p:?}"))
88 }
89 },
90 _ => Err(format!("invalid tuple data {x:?}"))
91 }
92 }
93 }
94
95
96impl<T1,T2> ToPlutusData for (T1,T2) where T1: ToPlutusData , T2: ToPlutusData {
97 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
98 let k = self.0.to_plutus_data(attribs)?;
99 let v = self.1.to_plutus_data(attribs)?;
100 Ok(CustomPlutus::make_tup(k,v))
101
102 }
103}
104
105
106impl<K : ToPlutusData + Clone,V : ToPlutusData + Clone> ToPlutusData for HashMap<K,V> {
107 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
108 if let Some(p) = CustomPlutus::make_map(self,attribs)?.as_pallas() {
109 Ok(p.clone())
110 } else {
111 Err(String::from("to_plutus_data for hashmap failed."))
112 }
113 }
114}
115
116impl<K : ToPlutusData + Clone + Ord,V : ToPlutusData + Clone + Ord> ToPlutusData for BTreeMap<K,V> {
117 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
118 if let Some(p) = CustomPlutus::make_bt_map(self,attribs)?.as_pallas() {
119 Ok(p.clone())
120 } else {
121 Err(String::from("to_plutus_data for btreemap failed."))
122 }
123 }
124}
125
126
127impl<T : FromPlutusData<T>> FromPlutusData<Box<T>> for Box<T> {
128 fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<Box<T>,String> {
129 let result = T::from_plutus_data(x,attribs);
130 match result {
131 Ok(v) => Ok(Box::new(v)),
132 Err(e) => Err(format!("Failed to unpack option value from plutus data! Error: {}",e))
133 }
134 }
135}
136
137impl<T : ToPlutusData + Clone + std::fmt::Debug> ToPlutusData for Vec<T> {
138 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
139 if let Some(p) = CustomPlutus::make_list(self,attribs)?.as_pallas() {
140 Ok(p.clone())
141 } else {
142 Err(String::from("to_plutus_data for vec<T> failed."))
143 }
144 }
145}
146
147impl<T1 :std::hash::Hash + std::cmp::Eq + FromPlutusData<T1>,T2 : FromPlutusData<T2>> FromPlutusData<HashMap<T1,T2>> for HashMap<T1,T2> {
148 fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<HashMap<T1,T2>,String> {
149 match p {
150 PlutusData::Map(m) => {
151 let mut result = HashMap::new();
152 for kvp in m.iter() {
153
154 let the_key = kvp.0.clone();
155 let k = T1::from_plutus_data(the_key.clone(),attribs);
156
157 let the_val = kvp.1.clone();
158 let v = T2::from_plutus_data(the_val.clone(),attribs);
159
160
161 result.insert(k?,v?);
165 }
166
167 Ok(result)
168 },
169 _ => Err(format!("Attempting to decode a hashmap but instead found: {:?}.",p))
170 }
171 }
172}
173
174
175impl<T1 : Ord + std::cmp::Eq + FromPlutusData<T1>,T2 : FromPlutusData<T2>> FromPlutusData<BTreeMap<T1,T2>> for BTreeMap<T1,T2> {
176 fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<BTreeMap<T1,T2>,String> {
177 match p {
178 PlutusData::Map(m) => {
179 let mut result = BTreeMap::new();
180 for kvp in m.iter() {
181
182 let the_key = kvp.0.clone();
183 let k = T1::from_plutus_data(the_key.clone(),attribs);
184
185 let the_val = kvp.1.clone();
186 let v = T2::from_plutus_data(the_val.clone(),attribs);
187
188 result.insert(k?,v?);
189 }
190
191 Ok(result)
192 },
193 _ => Err(format!("Attempting to decode a btreemap but instead found: {:?}.",p))
194 }
195 }
196}
197
198impl<T : FromPlutusData<T>> FromPlutusData<Vec<T>> for Vec<T> {
199 fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<Vec<T>,String> {
200 match p {
201 PlutusData::Array(pl) => {
202 let mut result : Vec<T> = vec![];
203 for x in pl {
204 match T::from_plutus_data(x,attribs) {
205 Ok(v) => { result.push(v) },
206 Err(e) => return Err(format!("when decoding a vector, we got this error: {}",e))
207 }
208 }
209
210 Ok(result)
211 },
212 _ => Err(String::from("Failed to decode vec from plutus data because it was not a plutus list."))
213 }
214
215 }
216}
217
218impl FromPlutusData<String> for String {
219 fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<String,String> {
220 let b16 : bool = attribs.iter().any(|a|a.to_lowercase() == "base_16");
221 match x {
222 PlutusData::BoundedBytes(bytes) if b16 => {
223 Ok(bytes.encode_hex())
224 },
225 PlutusData::BoundedBytes(bytes) => {
226 match std::str::from_utf8(&bytes) {
227 Ok(s) => Ok(s.to_owned()),
228 Err(e) => Err(format!("{:?}",e))
229 }
230 },
231 _ => Err(format!("expected string bytes, found something else: {:?}..",x))
232 }
233 }
234}
235
236impl ToPlutusData for String {
237 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
238 let b16 : bool = attribs.iter().any(|a|a.to_lowercase() == "base_16");
239 let bytes = String::as_bytes(self).to_vec();
240 if b16 {
241 match hex::decode(bytes) {
242 Ok(hex_bytes) => Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(hex_bytes.into())),
243 Err(e) => Err(format!("{:?}",e))
244 }
245 } else {
246 Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(bytes.into()))
247 }
248 }
249}
250
251impl<T: ToPlutusData> ToPlutusData for &Option<T> {
252 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
253 let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
254 match self {
255 None if ignore_option_container => Err(String::from("Not possible to encode &None to plutus data when using attribute 'ignore_option_container'.")),
256 Some(v) if ignore_option_container => v.to_plutus_data(attribs),
257 None => Ok(empty_constr(1)),
258 Some(v) => {
259 Ok(
260 wrap_with_constr(
261 0,
262 v.to_plutus_data(attribs)?
263 )
264 )
265 }
266 }
267 }
268}
269
270pub struct ByteVec(Vec<u8>);
271
272impl ToPlutusData for ByteVec {
273 fn to_plutus_data(&self,_attributes:&[String]) -> Result<PlutusData,String> {
274 Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(self.0.clone().into()))
275 }
276}
277
278impl<T : FromPlutusData<T>> FromPlutusData<Option<T>> for Option<T> {
279 fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<Option<T>,String> {
280 let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
281
282 if ignore_option_container {
283 let result = T::from_plutus_data(x,attribs);
284 match result {
285 Ok(v) => Ok(Some(v)),
286 Err(e) => Err(format!("Failed to unpack (ignore_option_container) option value from plutus data! Error: {}",e))
287 }
288 } else {
289 match x {
290 PlutusData::Constr(c) => {
291 let constr = match c.tag {
294 121..=127 => c.tag - 121,
295 1280..=1400 => c.tag - 1280 + 7,
296 102=> if let Some(xq) = c.any_constructor { xq } else {
297 return Err(format!("constructor 102 was not expected"));
298 },
299 xxx => return Err(format!("Unexpected constructor {} when decoding an item of type {}",xxx,std::any::type_name::<T>()))
300 };
301
302 match (constr,c.fields.len()) {
304 (0,1) => {
305 Ok(Some(T::from_plutus_data(c.fields[0].clone(),attribs)?))
306 },
307 (1,0) => Ok(None),
308 _ => {
309 Err(String::from("failed to unpack option value. not valid const representation."))
310 }
311 }
312 },
313 _ => Err(format!("failed to decode option value form plutus data... expected constr, found: {:?}",x)),
314 }
315 }
316 }
317}
318
319impl<T: ToPlutusData> ToPlutusData for Option<T> {
320 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
321 let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
322 match self {
323 None if ignore_option_container => Err(String::from("Not possible to encode None to plutus data when using attribute 'ignore_option_container'.")),
324 Some(v) if ignore_option_container => {
325 v.to_plutus_data(attribs)
327 },
328 None => Ok(empty_constr(1)),
329 Some(v) => {
330 Ok(
332 wrap_with_constr(
333 0,
334 v.to_plutus_data(attribs)?
335 )
336 )
337 }
338 }
339 }
340}
341
342impl<T: ToPlutusData + Clone + ?Sized> ToPlutusData for Box<T> {
343 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
344 let inner_item : T = Box::into_inner(self.to_owned());
345 inner_item.to_plutus_data(attribs)
346 }
347}
348
349
350impl FromPlutusData<bool> for bool {
351 fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<bool,String> {
352 let num_rep : bool = attribs.iter().any(|a|a.to_lowercase() == "repr_bool_as_num");
353 if num_rep {
354 match x {
355 PlutusData::BigInt(n) => {
356 let s = match n {
357 pallas_primitives::babbage::BigInt::Int(nn) => nn.0.to_string(),
358 BigInt::BigUInt(nn) => nn.to_string(),
359 BigInt::BigNInt(nn) => nn.to_string(),
360 };
361 Ok(s=="1")
362 },
363 PlutusData::Constr(c) => {
364 Err(format!("cannot decode bool using repr_bool_as_num. it seems to be encoded as a constr, perhaps you did not mean to apply repr_bool_as_num? {:?}",c))
365 }
366 _ => Err(format!("cannot decode bool with num_rep from {:?}",x))
367 }
368 } else {
369 match x {
370 PlutusData::Constr(c) => {
371
372 let t = match c.tag {
373 121..=127 => c.tag - 121,
374 1280..=1400 => c.tag - 1280 + 7,
375 102 => if let Some(xq) = c.any_constructor { xq } else {
376 return Err(format!("constructor 102 was not expected"));
377 },
378 xxx => return Err(format!("Unexpected constructor for {:?}",xxx))
379 };
380
381 if t == 1 {
382 return Ok(true);
383 }
384
385 if t == 0 {
386 return Ok(false);
387 }
388
389 return Err(format!("Unexpected constructor a boolean! expected 1 or 0 but found: {t}"))
390 }
391 _ => {
392 match x {
393 PlutusData::BigInt(n) => {
394 Err(format!("failed to decode this plutus data to bool using constr repr. it does seem to be a valid integer encoded bool, perhaps try with using repr_bool_as_num? --> {:?}",n))
395 },
396 _ => Err(format!("cannot decode bool using constr_rep from {:?}",x))
397 }
398
399 }
400 }
401 }
402
403 }
404}
405
406impl ToPlutusData for bool {
407 fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
408 let num_rep : bool = attribs.iter().any(|a|a.to_lowercase() == "repr_bool_as_num");
409 match self {
410 true if num_rep => Ok(CustomPlutus::big_int(1).as_pallas().unwrap().clone()), false if num_rep => Ok(CustomPlutus::big_int(0).as_pallas().unwrap().clone()), true => Ok(CustomPlutus::make_constr(1, vec![])),
413 false => Ok(CustomPlutus::make_constr(0, vec![])),
414 }
415 }
416}
417
418
419
420ImplPlutusForNum!(@i8);
421ImplPlutusForNum!(@i16);
422ImplPlutusForNum!(@i32);
423ImplPlutusForNum!(@i64);
424ImplPlutusForNum!(@i128);
425ImplPlutusForNum!(@u8);
426ImplPlutusForNum!(@u16);
427ImplPlutusForNum!(@u32);
428ImplPlutusForNum!(@u64);
429ImplPlutusForNum!(@u128);
430ImplPlutusForNum!(@usize);
431
432use pallas_primitives::babbage::BigInt;
433pub fn convert_to_big_int(i:&i64) -> BigInt { CustomPlutus::to_big_int(*i) }
434pub fn convert_u64_to_big_int(i:&u64) -> BigInt { CustomPlutus::to_big_uint(*i) }
435pub fn convert_to_big_i128(i:&i128) -> Result<BigInt,String> { CustomPlutus::to_big_int128(*i) }
436pub fn convert_u64_to_u128(i:&u128) -> Result<BigInt,String> { CustomPlutus::to_big_uint128(*i) }
437
438mod macros {
439 #[macro_export]
440 #[doc(hidden)]
441 macro_rules! ImplPlutusForNum {
442 (@$T:ident) => {
443 impl ToPlutusData for $T {
444 fn to_plutus_data(&self,_attribs:&[String]) -> Result<PlutusData,String> {
445 match &self.to_string().parse::<i128>() {
446 Ok(big_number) => {
447 let num = pallas_codec::utils::Int::try_from(*big_number).map_err(|e|format!("{e:?}"))?;
448 Ok(PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(num)))
449 },
450 Err(e) => Err(format!("failed to parse {} to BigInt. {e:?}",self)),
451 }
452 }
453 }
454 impl ToPlutusData for &$T {
455 fn to_plutus_data(&self,_attribs:&[String]) -> Result<PlutusData,String> {
456 match &self.to_string().parse::<i128>() {
457 Ok(big_number) => {
458 let num = pallas_codec::utils::Int::try_from(*big_number).map_err(|e|format!("{e:?}"))?;
459 Ok(PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(num)))
460 }
461 Err(_) => Err(format!("failed to parse {} to BigInt.",self)),
462 }
463 }
464 }
465 impl FromPlutusData<$T> for $T {
466 fn from_plutus_data(p:PlutusData,_attribs:&[String]) -> Result<$T,String> {
467 match p {
468 PlutusData::BigInt(n) => {
469 let s = match n {
470 pallas_primitives::babbage::BigInt::Int(nn) => nn.0.to_string(),
471 pallas_primitives::babbage::BigInt::BigUInt(nn) => nn.to_string(),
472 pallas_primitives::babbage::BigInt::BigNInt(nn) => nn.to_string()
473 };
474 match s.parse::<$T>() {
475 Ok(vc) => Ok(vc),
476 Err(e) => Err(format!("Failed to convert string to number. {}.",e)),
477 }
478 },
479 _ => Err(format!("failed to parse plutus data to num! input kind: {:?} - inner plutus data: {:?}",p,p)),
480 }
481 }
482 }
483 }
484 }
485}
486
487fn empty_constr(tag_num: u64) -> PlutusData {
488 CustomPlutus::make_constr(tag_num, vec![])
489}
490
491fn wrap_with_constr(tag_num: u64, data: PlutusData) -> PlutusData {
492 CustomPlutus::make_constr(tag_num, vec![data])
493}