1mod primitives;
2
3#[cfg(feature = "derive")]
4pub use plutus_parser_derive::*;
5
6#[cfg(feature = "pallas-v0_32")]
7pub use pallas_v0_32::{
8 BigInt, BoundedBytes, Constr, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
9};
10
11#[cfg(feature = "pallas-v0_33")]
12pub use pallas_v0_33::{
13 BigInt, BoundedBytes, Constr, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
14};
15
16#[cfg(feature = "pallas-v1")]
17pub use pallas_v1::{
18 BigInt, BoundedBytes, Constr, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
19};
20
21use thiserror::Error;
22
23#[derive(Error, Debug)]
24pub enum DecodeError {
25 #[error("unexpected variant {variant}")]
26 UnexpectedVariant { variant: u64 },
27 #[error("unexpected type (expected {expected}, found {actual})")]
28 UnexpectedType { expected: String, actual: String },
29 #[error("unexpected field count for tuple (expected {expected}, found {actual})")]
30 WrongTupleFieldCount { expected: usize, actual: usize },
31 #[error("unexpected field count for variant {variant} (expected {expected}, found {actual})")]
32 WrongVariantFieldCount {
33 variant: u64,
34 expected: usize,
35 actual: usize,
36 },
37 #[error("{0}")]
38 Custom(String),
39}
40
41pub trait AsPlutus: Sized {
42 fn from_plutus(data: PlutusData) -> Result<Self, DecodeError>;
43 fn to_plutus(self) -> PlutusData;
44
45 fn vec_from_plutus(data: PlutusData) -> Result<Vec<Self>, DecodeError> {
46 let items = parse_array(data)?;
47 items.into_iter().map(Self::from_plutus).collect()
48 }
49
50 fn vec_to_plutus(value: Vec<Self>) -> PlutusData {
51 create_array(value.into_iter().map(Self::to_plutus).collect())
52 }
53}
54
55pub fn parse_array(data: PlutusData) -> Result<Vec<PlutusData>, DecodeError> {
56 let array = match data {
57 PlutusData::Array(array) => array,
58 other => {
59 return Err(DecodeError::UnexpectedType {
60 expected: "Array".to_string(),
61 actual: type_name(&other),
62 });
63 }
64 };
65 Ok(array.to_vec())
66}
67
68pub fn parse_tuple<const N: usize>(data: PlutusData) -> Result<[PlutusData; N], DecodeError> {
69 let array = parse_array(data)?;
70 array
71 .try_into()
72 .map_err(|f: Vec<PlutusData>| DecodeError::WrongTupleFieldCount {
73 expected: N,
74 actual: f.len(),
75 })
76}
77
78pub fn parse_constr(data: PlutusData) -> Result<(u64, Vec<PlutusData>), DecodeError> {
79 let constr = match data {
80 PlutusData::Constr(constr) => constr,
81 other => {
82 return Err(DecodeError::UnexpectedType {
83 expected: "Constr".to_string(),
84 actual: type_name(&other),
85 });
86 }
87 };
88 let Some(variant) = constr.constructor_value() else {
89 return Err(DecodeError::Custom("value has invalid tag".to_string()));
90 };
91 Ok((variant, constr.fields.to_vec()))
92}
93
94pub fn parse_variant<const N: usize>(
95 variant: u64,
96 fields: Vec<PlutusData>,
97) -> Result<[PlutusData; N], DecodeError> {
98 fields
99 .try_into()
100 .map_err(|f: Vec<PlutusData>| DecodeError::WrongVariantFieldCount {
101 variant,
102 expected: N,
103 actual: f.len(),
104 })
105}
106
107pub fn parse_map(data: PlutusData) -> Result<Vec<(PlutusData, PlutusData)>, DecodeError> {
108 let kvps = match data {
109 PlutusData::Map(kvps) => kvps,
110 other => {
111 return Err(DecodeError::UnexpectedType {
112 expected: "Map".to_string(),
113 actual: type_name(&other),
114 });
115 }
116 };
117 Ok(kvps.to_vec())
118}
119
120pub fn create_constr(variant: u64, fields: Vec<PlutusData>) -> PlutusData {
121 let (tag, any_constructor) = match variant {
122 0..=6 => (variant + 121, None),
123 7..=127 => (variant + 1280 - 7, None),
124 x => (102, Some(x)),
125 };
126 PlutusData::Constr(Constr {
127 tag,
128 any_constructor,
129 fields: if !fields.is_empty() {
130 MaybeIndefArray::Indef(fields)
131 } else {
132 MaybeIndefArray::Def(Vec::new())
133 },
134 })
135}
136
137pub fn create_array(fields: Vec<PlutusData>) -> PlutusData {
138 PlutusData::Array(if !fields.is_empty() {
139 MaybeIndefArray::Indef(fields)
140 } else {
141 MaybeIndefArray::Def(Vec::new())
142 })
143}
144
145pub fn create_map(kvps: Vec<(PlutusData, PlutusData)>) -> PlutusData {
146 PlutusData::Map(KeyValuePairs::Def(kvps))
147}
148
149pub(crate) fn type_name(data: &PlutusData) -> String {
150 match data {
151 PlutusData::Array(_) => "Array",
152 PlutusData::BigInt(_) => "BigInt",
153 PlutusData::BoundedBytes(_) => "BoundedBytes",
154 PlutusData::Constr(_) => "Constr",
155 PlutusData::Map(_) => "Map",
156 }
157 .to_string()
158}