plutus_parser/
lib.rs

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