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, Hash, 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, Hash, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
18};
19
20#[cfg(feature = "pallas-v0_34")]
21pub use minicbor_v0_25 as minicbor;
22#[cfg(feature = "pallas-v0_34")]
23pub use pallas_v0_34::{
24 BigInt, BoundedBytes, Constr, Hash, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
25};
26
27#[cfg(feature = "pallas-v1")]
28pub use minicbor_v0_26 as minicbor;
29#[cfg(feature = "pallas-v1")]
30pub use pallas_v1::{
31 BigInt, BoundedBytes, Constr, Hash, Int, KeyValuePairs, MaybeIndefArray, PlutusData,
32};
33
34use thiserror::Error;
35
36#[derive(Error, Debug, PartialEq, Eq)]
37#[error("decode error at {path}: {kind}")]
38pub struct DecodeError {
39 kind: DecodeErrorKind,
40 path: String,
41}
42
43impl DecodeError {
44 pub fn new(kind: DecodeErrorKind) -> Self {
45 Self {
46 kind,
47 path: String::new(),
48 }
49 }
50
51 pub fn unexpected_variant(variant: u64) -> Self {
52 Self::new(DecodeErrorKind::UnexpectedVariant { variant })
53 }
54
55 pub fn unexpected_type<E: Into<String>, A: Into<String>>(expected: E, actual: A) -> Self {
56 Self::new(DecodeErrorKind::UnexpectedType {
57 expected: expected.into(),
58 actual: actual.into(),
59 })
60 }
61
62 pub fn wrong_length(expected: usize, actual: usize) -> Self {
63 Self::new(DecodeErrorKind::WrongLength { expected, actual })
64 }
65
66 pub fn wrong_tuple_field_count(expected: usize, actual: usize) -> Self {
67 Self::new(DecodeErrorKind::WrongTupleFieldCount { expected, actual })
68 }
69
70 pub fn wrong_variant_field_count(variant: u64, expected: usize, actual: usize) -> Self {
71 Self::new(DecodeErrorKind::WrongVariantFieldCount {
72 variant,
73 expected,
74 actual,
75 })
76 }
77
78 pub fn out_of_range(value: impl std::fmt::Display) -> Self {
79 Self::new(DecodeErrorKind::OutOfRange {
80 value: value.to_string(),
81 })
82 }
83
84 pub fn invalid_cbor(error: minicbor::decode::Error) -> Self {
85 Self::new(DecodeErrorKind::InvalidCbor(MinicborDecodeError(error)))
86 }
87
88 pub fn custom(message: impl Into<String>) -> Self {
89 Self::new(DecodeErrorKind::Custom(message.into()))
90 }
91
92 pub fn with_field_name(mut self, name: impl std::fmt::Display) -> Self {
93 if self.path.is_empty() {
94 self.path = name.to_string();
95 } else if self.path.starts_with("[") || self.path.starts_with(":") {
96 self.path = format!("{name}{}", self.path);
97 } else {
98 self.path = format!("{name}.{}", self.path);
99 }
100 self
101 }
102}
103
104#[derive(Error, Debug, PartialEq, Eq)]
105pub enum DecodeErrorKind {
106 #[error("unexpected variant {variant}")]
107 UnexpectedVariant { variant: u64 },
108 #[error("unexpected type (expected {expected}, found {actual})")]
109 UnexpectedType { expected: String, actual: String },
110 #[error("unexpected length for array (expected {expected}, found {actual})")]
111 WrongLength { expected: usize, actual: usize },
112 #[error("unexpected field count for tuple (expected {expected}, found {actual})")]
113 WrongTupleFieldCount { expected: usize, actual: usize },
114 #[error("unexpected field count for variant {variant} (expected {expected}, found {actual})")]
115 WrongVariantFieldCount {
116 variant: u64,
117 expected: usize,
118 actual: usize,
119 },
120 #[error("value {value} out of range")]
121 OutOfRange { value: String },
122 #[error("invalid cbor: {0}")]
123 InvalidCbor(MinicborDecodeError),
124 #[error("{0}")]
125 Custom(String),
126}
127
128#[derive(Error, Debug)]
129#[error("{0}")]
130pub struct MinicborDecodeError(minicbor::decode::Error);
131impl PartialEq for MinicborDecodeError {
132 fn eq(&self, other: &Self) -> bool {
133 self.0.to_string() == other.0.to_string()
134 }
135}
136impl Eq for MinicborDecodeError {}
137
138pub trait AsPlutus: Sized {
139 fn from_plutus(data: PlutusData) -> Result<Self, DecodeError>;
140 fn to_plutus(self) -> PlutusData;
141
142 fn from_plutus_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
143 let data = minicbor::decode::<PlutusData>(bytes).map_err(DecodeError::invalid_cbor)?;
144 Self::from_plutus(data)
145 }
146
147 fn to_plutus_bytes(self) -> Vec<u8> {
148 let data = self.to_plutus();
149 minicbor::to_vec(data).expect("infallible")
150 }
151
152 fn vec_from_plutus(data: PlutusData) -> Result<Vec<Self>, DecodeError> {
153 let items = parse_array(data)?;
154 items
155 .into_iter()
156 .enumerate()
157 .map(|(index, item)| {
158 Self::from_plutus(item).map_err(|e| e.with_field_name(format!("[{index}]")))
159 })
160 .collect()
161 }
162
163 fn vec_to_plutus(value: Vec<Self>) -> PlutusData {
164 create_array(value.into_iter().map(Self::to_plutus).collect())
165 }
166
167 fn array_from_plutus<const N: usize>(data: PlutusData) -> Result<[Self; N], DecodeError> {
168 let items = parse_array(data)?;
169 if items.len() != N {
170 return Err(DecodeError::wrong_length(N, items.len()));
171 }
172 let result = items
173 .into_iter()
174 .enumerate()
175 .map(|(index, item)| {
176 Self::from_plutus(item).map_err(|e| e.with_field_name(format!("[{index}]")))
177 })
178 .collect::<Result<Vec<Self>, DecodeError>>()?;
179
180 result
181 .try_into()
182 .map_err(|e: Vec<_>| DecodeError::wrong_length(N, e.len()))
183 }
184
185 fn array_to_plutus<const N: usize>(value: [Self; N]) -> PlutusData {
186 create_array(value.into_iter().map(Self::to_plutus).collect())
187 }
188}
189
190pub fn parse_array(data: PlutusData) -> Result<Vec<PlutusData>, DecodeError> {
191 let array = match data {
192 PlutusData::Array(array) => array,
193 other => {
194 return Err(DecodeError::unexpected_type("Array", type_name(&other)));
195 }
196 };
197 Ok(array.to_vec())
198}
199
200pub fn parse_tuple<const N: usize>(data: PlutusData) -> Result<[PlutusData; N], DecodeError> {
201 let array = parse_array(data)?;
202 array
203 .try_into()
204 .map_err(|f: Vec<PlutusData>| DecodeError::wrong_tuple_field_count(N, f.len()))
205}
206
207pub fn parse_constr(data: PlutusData) -> Result<(u64, Vec<PlutusData>), DecodeError> {
208 let constr = match data {
209 PlutusData::Constr(constr) => constr,
210 other => {
211 return Err(DecodeError::unexpected_type("Constr", type_name(&other)));
212 }
213 };
214 let Some(variant) = constr.constructor_value() else {
215 return Err(DecodeError::custom("value has invalid tag"));
216 };
217 Ok((variant, constr.fields.to_vec()))
218}
219
220pub fn parse_variant<const N: usize>(
221 variant: u64,
222 fields: Vec<PlutusData>,
223) -> Result<[PlutusData; N], DecodeError> {
224 fields
225 .try_into()
226 .map_err(|f: Vec<PlutusData>| DecodeError::wrong_variant_field_count(variant, N, f.len()))
227}
228
229pub fn parse_map(data: PlutusData) -> Result<Vec<(PlutusData, PlutusData)>, DecodeError> {
230 let kvps = match data {
231 PlutusData::Map(kvps) => kvps,
232 other => {
233 return Err(DecodeError::unexpected_type("Map", type_name(&other)));
234 }
235 };
236 Ok(kvps.to_vec())
237}
238
239pub fn create_constr(variant: u64, fields: Vec<PlutusData>) -> PlutusData {
240 let (tag, any_constructor) = match variant {
241 0..=6 => (variant + 121, None),
242 7..=127 => (variant + 1280 - 7, None),
243 x => (102, Some(x)),
244 };
245 PlutusData::Constr(Constr {
246 tag,
247 any_constructor,
248 fields: if !fields.is_empty() {
249 MaybeIndefArray::Indef(fields)
250 } else {
251 MaybeIndefArray::Def(Vec::new())
252 },
253 })
254}
255
256pub fn create_array(fields: Vec<PlutusData>) -> PlutusData {
257 PlutusData::Array(if !fields.is_empty() {
258 MaybeIndefArray::Indef(fields)
259 } else {
260 MaybeIndefArray::Def(Vec::new())
261 })
262}
263
264pub fn create_map(kvps: Vec<(PlutusData, PlutusData)>) -> PlutusData {
265 PlutusData::Map(KeyValuePairs::Def(kvps))
266}
267
268pub(crate) fn type_name(data: &PlutusData) -> &str {
269 match data {
270 PlutusData::Array(_) => "Array",
271 PlutusData::BigInt(_) => "BigInt",
272 PlutusData::BoundedBytes(_) => "BoundedBytes",
273 PlutusData::Constr(_) => "Constr",
274 PlutusData::Map(_) => "Map",
275 }
276}