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-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}