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