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