Skip to main content

pallas_codec/
lib.rs

1/// Flat encoding/decoding for Plutus Core
2pub mod flat;
3
4/// Shared re-export of minicbor lib across all Pallas
5pub use minicbor;
6
7/// Round-trip friendly common helper structs
8pub mod utils;
9
10pub trait Fragment: Sized + for<'b> minicbor::Decode<'b, ()> + minicbor::Encode<()> {}
11
12impl<T> Fragment for T where T: for<'b> minicbor::Decode<'b, ()> + minicbor::Encode<()> + Sized {}
13
14#[macro_export]
15macro_rules! codec_by_datatype {
16    (
17        $enum_name:ident $( < $lifetime:lifetime > )?,
18        $( $( $cbortype:ident )|* => $one_f:ident ),*,
19        ($( $( $vars:ident ),+ => $many_f:ident )?)
20    ) => {
21        impl<$( $lifetime, )? '__b $(:$lifetime)?,  C> minicbor::decode::Decode<'__b, C> for $enum_name $(<$lifetime>)? {
22            fn decode(d: &mut minicbor::Decoder<'__b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
23                match d.datatype()? {
24                    $( minicbor::data::Type::Array => {
25                        d.array()?;
26                        // Using the identifiers trivially to ensure repetition.
27                        Ok($enum_name::$many_f($({ let $vars = d.decode_with(ctx)?; $vars }, )+ ))
28                    }, )?
29                    $( $( minicbor::data::Type::$cbortype )|* => Ok($enum_name::$one_f(d.decode_with(ctx)?)), )*
30                    _ => Err(minicbor::decode::Error::message(
31                            "Unknown cbor data type for this macro-defined enum.")
32                    ),
33                }
34            }
35        }
36
37        impl< $( $lifetime, )? C> minicbor::encode::Encode<C> for $enum_name $(<$lifetime>)?  {
38            fn encode<W: minicbor::encode::Write>(
39                &self,
40                e: &mut minicbor::Encoder<W>,
41                ctx: &mut C,
42            ) -> Result<(), minicbor::encode::Error<W::Error>> {
43                match self {
44                    $( $enum_name::$many_f ($( $vars ),+) => {
45                        // Counting the number of `$vars`:
46                        let length: u64 = 0 $(+ { let _ = $vars; 1 })+;
47                        e.array(length)?;
48                        $( e.encode_with($vars, ctx)?; )+
49                    }, )?
50                    $( $enum_name::$one_f(__x666) => {
51                        e.encode_with(__x666, ctx)?;
52                    } )*
53                };
54
55                Ok(())
56            }
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::minicbor::{self, decode, encode, Decode, Encode};
64
65    #[derive(Clone, Debug)]
66    enum Thing {
67        Coin(u32),
68        Change(bool),
69        Multiasset(bool, u64, i32),
70    }
71
72    codec_by_datatype! {
73        Thing,
74        U8 | U16 | U32 => Coin,
75        Bool => Change,
76        (b, u, i => Multiasset)
77    }
78
79    #[cfg(test)]
80    pub fn roundtrip_codec<T: Encode<()> + for<'a> Decode<'a, ()> + std::fmt::Debug>(
81        query: T,
82    ) -> () {
83        let mut cbor = Vec::new();
84        match encode(query, &mut cbor) {
85            Ok(_) => (),
86            Err(err) => panic!("Unable to encode data ({:?})", err),
87        };
88        println!("{:-<70}\nResulting CBOR: {:02x?}", "", cbor);
89
90        let query: T = decode(&cbor).unwrap();
91        println!("Decoded data: {:?}", query);
92    }
93
94    #[test]
95    fn roundtrip_codec_by_datatype() {
96        roundtrip_codec(Thing::Coin(0xfafa));
97        roundtrip_codec(Thing::Change(false));
98        roundtrip_codec(Thing::Multiasset(true, 10, -20));
99    }
100}