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