1use std::collections::{BTreeMap, BTreeSet};
4
5use cardano_serialization_lib as csl;
6use num_bigint::BigInt;
7
8use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA};
9use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL};
10
11pub use is_plutus_data_derive::IsPlutusData;
12
13#[cfg(feature = "lbf")]
14use data_encoding::HEXLOWER;
15#[cfg(feature = "lbf")]
16use lbr_prelude::error::Error;
17#[cfg(feature = "lbf")]
18use lbr_prelude::json::{
19 case_json_constructor, case_json_object, json_constructor, json_object, Json,
20};
21
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum PlutusData {
29 Constr(BigInt, Vec<PlutusData>),
30 Map(Vec<(PlutusData, PlutusData)>),
31 List(Vec<PlutusData>),
32 Integer(BigInt),
33 Bytes(Vec<u8>),
34}
35
36#[derive(Clone, Debug)]
37pub enum PlutusType {
38 Constr,
39 Map,
40 List,
41 Integer,
42 Bytes,
43}
44
45pub trait IsPlutusData {
46 fn to_plutus_data(&self) -> PlutusData;
47
48 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError>
49 where
50 Self: Sized;
51}
52
53#[derive(Clone, Debug, thiserror::Error)]
55pub enum PlutusDataError {
56 #[error("Expected a PlutusData type {wanted:?}, but got {got:?}")]
57 UnexpectedPlutusType { got: PlutusType, wanted: PlutusType },
58 #[error("Expected a PlutusData type as {wanted:?}, but got {got:?}")]
59 UnexpectedPlutusInvariant { got: String, wanted: String },
60 #[error("Expected a Plutus List with {wanted:?} elements, but got {got:?} elements")]
61 UnexpectedListLength { got: usize, wanted: usize },
62 #[error("Some internal error happened: {0}")]
63 InternalError(String),
64}
65
66impl From<&PlutusData> for PlutusType {
67 fn from(plutus_data: &PlutusData) -> Self {
68 match plutus_data {
69 PlutusData::Constr(_, _) => PlutusType::Constr,
70 PlutusData::Map(_) => PlutusType::Map,
71 PlutusData::List(_) => PlutusType::List,
72 PlutusData::Integer(_) => PlutusType::Integer,
73 PlutusData::Bytes(_) => PlutusType::Bytes,
74 }
75 }
76}
77
78impl PlutusData {
79 pub fn constr(tag: u32, fields: Vec<PlutusData>) -> Self {
80 PlutusData::Constr(BigInt::from(tag), fields)
81 }
82
83 pub fn map(fields: Vec<(PlutusData, PlutusData)>) -> Self {
84 PlutusData::Map(fields)
85 }
86
87 pub fn list(fields: Vec<PlutusData>) -> Self {
88 PlutusData::List(fields)
89 }
90
91 pub fn integer(value: u32) -> Self {
92 PlutusData::Integer(BigInt::from(value))
93 }
94
95 pub fn bytes(value: Vec<u8>) -> Self {
96 PlutusData::Bytes(value)
97 }
98}
99
100impl IsPlutusData for PlutusData {
101 fn to_plutus_data(&self) -> PlutusData {
102 self.clone()
103 }
104
105 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
106 Ok(plutus_data.clone())
107 }
108}
109
110impl TryFromCSL<csl::PlutusData> for PlutusData {
111 fn try_from_csl(value: &csl::PlutusData) -> Result<Self, TryFromCSLError> {
112 Ok(match value.kind() {
113 csl::PlutusDataKind::ConstrPlutusData => {
114 let constr_data = value.as_constr_plutus_data().unwrap();
115 let tag = BigInt::from_csl(&constr_data.alternative());
116 let args = constr_data.data().try_to_pla()?;
117 PlutusData::Constr(tag, args)
118 }
119 csl::PlutusDataKind::Map => PlutusData::Map(value.as_map().unwrap().try_to_pla()?),
120 csl::PlutusDataKind::List => PlutusData::List(value.as_list().unwrap().try_to_pla()?),
121 csl::PlutusDataKind::Integer => {
122 PlutusData::Integer(value.as_integer().unwrap().try_to_pla()?)
123 }
124 csl::PlutusDataKind::Bytes => PlutusData::Bytes(value.as_bytes().unwrap()),
125 })
126 }
127}
128
129#[cfg(feature = "lbf")]
130impl Json for PlutusData {
131 fn to_json(&self) -> serde_json::Value {
132 match self {
133 PlutusData::Constr(index, fields) => json_constructor(
134 "Constr",
135 vec![json_object(vec![
136 ("index".to_string(), index.to_json()),
137 ("fields".to_string(), fields.to_json()),
138 ])],
139 ),
140 PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]),
141 PlutusData::List(list) => json_constructor("List", vec![list.to_json()]),
142 PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]),
143 PlutusData::Bytes(bytes) => {
144 json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))])
145 }
146 }
147 }
148
149 fn from_json(value: &serde_json::Value) -> Result<PlutusData, Error> {
150 case_json_constructor(
151 "PlutusV1.PlutusData",
152 vec![
153 (
154 "Constr",
155 Box::new(|ctor_fields| match &ctor_fields[..] {
156 [val] => case_json_object(
157 |obj| {
158 let index = obj.get("index").ok_or(Error::UnexpectedFieldName {
159 wanted: "index".to_owned(),
160 got: obj.keys().cloned().collect(),
161 parser: "PlutusV1.PlutusData".to_owned(),
162 })?;
163
164 let fields =
165 obj.get("fields").ok_or(Error::UnexpectedFieldName {
166 wanted: "fields".to_owned(),
167 got: obj.keys().cloned().collect(),
168 parser: "PlutusV1.PlutusData".to_owned(),
169 })?;
170 Ok(PlutusData::Constr(
171 BigInt::from_json(index)?,
172 <Vec<PlutusData>>::from_json(fields)?,
173 ))
174 },
175 val,
176 ),
177 _ => Err(Error::UnexpectedArrayLength {
178 wanted: 1,
179 got: ctor_fields.len(),
180 parser: "PlutusV1.PlutusData".to_owned(),
181 }),
182 }),
183 ),
184 (
185 "Map",
186 Box::new(|ctor_fields| match &ctor_fields[..] {
187 [val] => Ok(PlutusData::Map(Json::from_json(val)?)),
188 _ => Err(Error::UnexpectedArrayLength {
189 wanted: 1,
190 got: ctor_fields.len(),
191 parser: "PlutusV1.PlutusData".to_owned(),
192 }),
193 }),
194 ),
195 (
196 "List",
197 Box::new(|ctor_fields| match &ctor_fields[..] {
198 [val] => Ok(PlutusData::List(Json::from_json(val)?)),
199 _ => Err(Error::UnexpectedArrayLength {
200 wanted: 1,
201 got: ctor_fields.len(),
202 parser: "PlutusV1.PlutusData".to_owned(),
203 }),
204 }),
205 ),
206 (
207 "Integer",
208 Box::new(|ctor_fields| match &ctor_fields[..] {
209 [val] => Ok(PlutusData::Integer(Json::from_json(val)?)),
210 _ => Err(Error::UnexpectedArrayLength {
211 wanted: 1,
212 got: ctor_fields.len(),
213 parser: "PlutusV1.PlutusData".to_owned(),
214 }),
215 }),
216 ),
217 (
218 "Bytes",
219 Box::new(|ctor_fields| match &ctor_fields[..] {
220 [val] => {
221 let bytes = String::from_json(val).and_then(|str| {
222 HEXLOWER.decode(&str.into_bytes()).map_err(|_| {
223 Error::UnexpectedJsonInvariant {
224 wanted: "base16 string".to_owned(),
225 got: "unexpected string".to_owned(),
226 parser: "Plutus.V1.Bytes".to_owned(),
227 }
228 })
229 })?;
230 Ok(PlutusData::Bytes(bytes))
231 }
232 _ => Err(Error::UnexpectedArrayLength {
233 wanted: 1,
234 got: ctor_fields.len(),
235 parser: "PlutusV1.PlutusData".to_owned(),
236 }),
237 }),
238 ),
239 ],
240 value,
241 )
242 }
243}
244
245impl IsPlutusData for BigInt {
248 fn to_plutus_data(&self) -> PlutusData {
249 PlutusData::Integer(self.clone())
250 }
251
252 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
253 match plutus_data {
254 PlutusData::Integer(int) => Ok(int.clone()),
255 _ => Err(PlutusDataError::UnexpectedPlutusType {
256 wanted: PlutusType::Integer,
257 got: PlutusType::from(plutus_data),
258 }),
259 }
260 }
261}
262
263impl IsPlutusData for Vec<u8> {
264 fn to_plutus_data(&self) -> PlutusData {
265 PlutusData::Bytes(self.clone())
266 }
267
268 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
269 match plutus_data {
270 PlutusData::Bytes(bytes) => Ok(bytes.clone()),
271 _ => Err(PlutusDataError::UnexpectedPlutusType {
272 wanted: PlutusType::Bytes,
273 got: PlutusType::from(plutus_data),
274 }),
275 }
276 }
277}
278
279const BOOL_FALSE_TAG: u32 = 0;
280const BOOL_TRUE_TAG: u32 = 1;
281
282impl IsPlutusData for bool {
283 fn to_plutus_data(&self) -> PlutusData {
284 if *self {
285 PlutusData::Constr(BOOL_TRUE_TAG.into(), vec![])
286 } else {
287 PlutusData::Constr(BOOL_FALSE_TAG.into(), vec![])
288 }
289 }
290
291 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
292 let (tag, fields) = parse_constr(plutus_data)?;
293 let [] = parse_fixed_len_constr_fields::<0>(fields)?;
294 match tag {
295 BOOL_TRUE_TAG => Ok(true),
296 BOOL_FALSE_TAG => Ok(false),
297 _ => Err(PlutusDataError::UnexpectedPlutusInvariant {
298 wanted: format!("Constr with tag {BOOL_TRUE_TAG} or {BOOL_FALSE_TAG}"),
299 got: tag.to_string(),
300 }),
301 }
302 }
303}
304
305impl IsPlutusData for String {
306 fn to_plutus_data(&self) -> PlutusData {
307 PlutusData::Bytes(self.as_bytes().into())
308 }
309
310 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
311 match plutus_data {
312 PlutusData::Bytes(bytes) => String::from_utf8(bytes.clone()).map_err(|err| {
313 PlutusDataError::InternalError(format!(
314 "Couldn't convert Plutus bytes to String: {:?}",
315 err
316 ))
317 }),
318 _ => Err(PlutusDataError::UnexpectedPlutusType {
319 wanted: PlutusType::Bytes,
320 got: PlutusType::from(plutus_data),
321 }),
322 }
323 }
324}
325
326impl IsPlutusData for char {
327 fn to_plutus_data(&self) -> PlutusData {
328 String::from(*self).to_plutus_data()
329 }
330
331 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
332 String::from_plutus_data(plutus_data).and_then(|str| {
333 let mut chars = str.chars();
334 let ch = chars.next();
335 let rest = chars.next();
336 match (ch, rest) {
337 (Some(ch), None) => Ok(ch),
338 _ => Err(PlutusDataError::UnexpectedPlutusInvariant {
339 got: "string".to_owned(),
340 wanted: "char".to_owned(),
341 }),
342 }
343 })
344 }
345}
346
347const OPTION_SOME_TAG: u32 = 0;
348const OPTION_NONE_TAG: u32 = 1;
349
350impl<T> IsPlutusData for Option<T>
351where
352 T: IsPlutusData,
353{
354 fn to_plutus_data(&self) -> PlutusData {
355 match self {
356 Some(val) => PlutusData::Constr(OPTION_SOME_TAG.into(), vec![val.to_plutus_data()]),
357 None => PlutusData::Constr(OPTION_NONE_TAG.into(), vec![]),
358 }
359 }
360
361 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
362 let (tag, fields) = parse_constr(plutus_data)?;
363
364 match tag {
365 OPTION_SOME_TAG => {
366 let [data] = parse_fixed_len_constr_fields::<1>(fields)?;
367 Ok(Some(T::from_plutus_data(data)?))
368 }
369 OPTION_NONE_TAG => {
370 let [] = parse_fixed_len_constr_fields::<0>(fields)?;
371 Ok(None)
372 }
373 _ => Err(PlutusDataError::UnexpectedPlutusInvariant {
374 wanted: format!("Constr with tag {OPTION_SOME_TAG} or {OPTION_NONE_TAG}"),
375 got: tag.to_string(),
376 }),
377 }
378 }
379}
380
381const RESULT_ERR_TAG: u32 = 0;
382const RESULT_OK_TAG: u32 = 1;
383
384impl<T, E> IsPlutusData for Result<T, E>
385where
386 T: IsPlutusData,
387 E: IsPlutusData,
388{
389 fn to_plutus_data(&self) -> PlutusData {
390 match self {
391 Err(err) => PlutusData::Constr(RESULT_ERR_TAG.into(), vec![err.to_plutus_data()]),
392 Ok(val) => PlutusData::Constr(RESULT_OK_TAG.into(), vec![val.to_plutus_data()]),
393 }
394 }
395
396 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
397 let (tag, fields) = parse_constr(plutus_data)?;
398 let [field] = parse_fixed_len_constr_fields::<1>(fields)?;
399
400 match tag {
401 RESULT_ERR_TAG => Ok(Err(E::from_plutus_data(field)?)),
402 RESULT_OK_TAG => Ok(Ok(T::from_plutus_data(field)?)),
403 _ => Err(PlutusDataError::UnexpectedPlutusInvariant {
404 wanted: format!("Constr with tag {RESULT_ERR_TAG} or {RESULT_OK_TAG}"),
405 got: tag.to_string(),
406 }),
407 }
408 }
409}
410
411impl<T> IsPlutusData for Vec<T>
412where
413 T: IsPlutusData,
414{
415 fn to_plutus_data(&self) -> PlutusData {
416 let values = self
417 .iter()
418 .map(|val| val.to_plutus_data())
419 .collect::<Vec<PlutusData>>();
420
421 PlutusData::List(values)
422 }
423
424 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
425 let list = parse_list(plutus_data)?;
426 list.iter().map(T::from_plutus_data).collect()
427 }
428}
429
430impl<T> IsPlutusData for BTreeSet<T>
431where
432 T: IsPlutusData + Eq + Ord,
433{
434 fn to_plutus_data(&self) -> PlutusData {
435 let set = self
436 .iter()
437 .map(|val| val.to_plutus_data())
438 .collect::<Vec<PlutusData>>();
439
440 PlutusData::List(set)
441 }
442
443 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
444 match plutus_data {
445 PlutusData::List(vec) => vec
446 .iter()
447 .map(|val| T::from_plutus_data(val))
448 .collect::<Result<Self, PlutusDataError>>(),
449 _ => Err(PlutusDataError::UnexpectedPlutusType {
450 wanted: PlutusType::List,
451 got: PlutusType::from(plutus_data),
452 }),
453 }
454 }
455}
456
457impl<K, V> IsPlutusData for BTreeMap<K, V>
458where
459 K: IsPlutusData + Eq + Ord,
460 V: IsPlutusData,
461{
462 fn to_plutus_data(&self) -> PlutusData {
463 let assoc_map = self
464 .iter()
465 .map(|(key, val)| (key.to_plutus_data(), val.to_plutus_data()))
466 .collect::<Vec<(PlutusData, PlutusData)>>();
467
468 PlutusData::Map(assoc_map)
469 }
470
471 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
472 match plutus_data {
473 PlutusData::Map(dict) => dict
474 .iter()
475 .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?)))
476 .collect::<Result<Self, PlutusDataError>>(),
477 _ => Err(PlutusDataError::UnexpectedPlutusType {
478 wanted: PlutusType::Map,
479 got: PlutusType::from(plutus_data),
480 }),
481 }
482 }
483}
484
485const UNIT_TAG: u32 = 0;
486
487impl IsPlutusData for () {
488 fn to_plutus_data(&self) -> PlutusData {
489 PlutusData::Constr(UNIT_TAG.into(), vec![])
490 }
491
492 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
493 let fields = parse_constr_with_tag(plutus_data, UNIT_TAG)?;
494 let [] = parse_fixed_len_constr_fields::<0>(fields)?;
495 Ok(())
496 }
497}
498
499const PAIR_TAG: u32 = 0;
500
501impl<A, B> IsPlutusData for (A, B)
502where
503 A: IsPlutusData,
504 B: IsPlutusData,
505{
506 fn to_plutus_data(&self) -> PlutusData {
507 PlutusData::Constr(
508 BigInt::from(PAIR_TAG),
509 vec![self.0.to_plutus_data(), self.1.to_plutus_data()],
510 )
511 }
512
513 fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
514 let fields = parse_constr_with_tag(plutus_data, PAIR_TAG)?;
515 let [a, b] = parse_fixed_len_constr_fields::<2>(fields)?;
516 Ok((A::from_plutus_data(a)?, B::from_plutus_data(b)?))
517 }
518}
519
520impl TryFromCSL<csl::PlutusList> for Vec<PlutusData> {
523 fn try_from_csl(value: &csl::PlutusList) -> Result<Self, TryFromCSLError> {
524 (0..value.len())
525 .map(|idx| value.get(idx).try_to_pla())
526 .collect()
527 }
528}
529
530impl TryFromCSL<csl::PlutusMap> for Vec<(PlutusData, PlutusData)> {
531 fn try_from_csl(c_map: &csl::PlutusMap) -> Result<Self, TryFromCSLError> {
532 let keys = c_map.keys();
533 (0..keys.len()).try_fold(Vec::new(), |mut vector, idx| {
534 let key = keys.get(idx);
535 let values = c_map.get(&key).unwrap();
536
537 for value_idx in 0..values.len() {
538 vector.push((
539 key.clone().try_to_pla()?,
540 values.get(value_idx).unwrap().try_to_pla()?,
541 ))
542 }
543
544 Ok(vector)
545 })
546 }
547}
548
549impl TryFromPLA<PlutusData> for csl::PlutusData {
550 fn try_from_pla(val: &PlutusData) -> Result<Self, TryFromPLAError> {
551 match val {
552 PlutusData::Constr(tag, args) => Ok(csl::PlutusData::new_constr_plutus_data(
553 &csl::ConstrPlutusData::new(&tag.try_to_csl()?, &args.try_to_csl()?),
554 )),
555 PlutusData::Map(l) => Ok(csl::PlutusData::new_map(&l.try_to_csl()?)),
556 PlutusData::List(l) => Ok(csl::PlutusData::new_list(&l.try_to_csl()?)),
557 PlutusData::Integer(i) => Ok(csl::PlutusData::new_integer(&i.try_to_csl()?)),
558 PlutusData::Bytes(b) => Ok(csl::PlutusData::new_bytes(b.to_owned())),
559 }
560 }
561}
562
563impl TryFromPLA<Vec<PlutusData>> for csl::PlutusList {
564 fn try_from_pla(val: &Vec<PlutusData>) -> Result<Self, TryFromPLAError> {
565 val.iter()
566 .map(|x| x.try_to_csl())
568 .collect::<Result<Vec<csl::PlutusData>, TryFromPLAError>>()
569 .map(|x| x.into())
570 }
571}
572
573impl TryFromPLA<Vec<(PlutusData, PlutusData)>> for csl::PlutusMap {
574 fn try_from_pla(val: &Vec<(PlutusData, PlutusData)>) -> Result<Self, TryFromPLAError> {
575 val.iter()
576 .try_fold(csl::PlutusMap::new(), |mut acc, (k, v)| {
577 let mut values = match acc.get(&k.try_to_csl()?) {
578 Some(existing_values) => existing_values,
579 None => csl::PlutusMapValues::new(),
580 };
581 values.add(&v.try_to_csl()?);
582 acc.insert(&k.try_to_csl()?, &values);
583 Ok(acc)
584 })
585 }
586}
587
588pub fn case_plutus_data<'a, T>(
592 ctor_case: impl FnOnce(&'a BigInt) -> Box<dyn 'a + FnOnce(&'a Vec<PlutusData>) -> T>,
593 list_case: impl FnOnce(&'a Vec<PlutusData>) -> T,
594 int_case: impl FnOnce(&'a BigInt) -> T,
595 other_case: impl FnOnce(&'a PlutusData) -> T,
596 pd: &'a PlutusData,
597) -> T {
598 match pd {
599 PlutusData::Constr(tag, args) => ctor_case(&tag)(&args),
600 PlutusData::List(args) => list_case(&args),
601 PlutusData::Integer(i) => int_case(&i),
602 other => other_case(&other),
603 }
604}
605
606pub fn parse_fixed_len_constr_fields<const LEN: usize>(
611 v: &[PlutusData],
612) -> Result<&[PlutusData; LEN], PlutusDataError> {
613 v.try_into()
614 .map_err(|_| PlutusDataError::UnexpectedListLength {
615 got: v.len(),
616 wanted: LEN,
617 })
618}
619
620pub fn parse_constr(data: &PlutusData) -> Result<(u32, &Vec<PlutusData>), PlutusDataError> {
625 match data {
626 PlutusData::Constr(tag, fields) => u32::try_from(tag)
627 .map_err(|err| PlutusDataError::UnexpectedPlutusInvariant {
628 got: err.to_string(),
629 wanted: "Constr bigint tag within u32 range".into(),
630 })
631 .map(|tag| (tag, fields)),
632 _ => Err(PlutusDataError::UnexpectedPlutusType {
633 wanted: PlutusType::Constr,
634 got: PlutusType::from(data),
635 }),
636 }
637}
638
639pub fn parse_constr_with_tag(
643 data: &PlutusData,
644 expected_tag: u32,
645) -> Result<&Vec<PlutusData>, PlutusDataError> {
646 let (tag, fields) = parse_constr(data)?;
647
648 if tag != expected_tag {
649 Err(PlutusDataError::UnexpectedPlutusInvariant {
650 got: tag.to_string(),
651 wanted: format!("Constr with tag {}", expected_tag),
652 })
653 } else {
654 Ok(fields)
655 }
656}
657
658pub fn parse_list(data: &PlutusData) -> Result<&Vec<PlutusData>, PlutusDataError> {
662 match data {
663 PlutusData::List(list_of_plutus_data) => Ok(list_of_plutus_data),
664 _ => Err(PlutusDataError::UnexpectedPlutusType {
665 got: PlutusType::from(data),
666 wanted: PlutusType::List,
667 }),
668 }
669}