data_anchor_utils/
encoding.rs

1#[derive(Debug, thiserror::Error)]
2pub enum DataAnchorEncodingError {
3    #[error("Postcard encoding error: {0}")]
4    Postcard(#[from] postcard::Error),
5
6    #[error("Bincode encoding error: {0}")]
7    Bincode(#[from] bincode::Error),
8
9    #[error("JSON encoding error: {0}")]
10    Json(#[from] serde_json::Error),
11
12    #[cfg(feature = "borsh")]
13    #[error("Borsh encoding error: {0}")]
14    Borsh(#[from] borsh::io::Error),
15}
16
17pub type DataAnchorEncodingResult<T = ()> = Result<T, DataAnchorEncodingError>;
18
19#[cfg(not(feature = "borsh"))]
20mod _no_borsh {
21
22    pub trait Encodable: serde::ser::Serialize {}
23
24    impl<T: serde::ser::Serialize> Encodable for T {}
25
26    pub trait Decodable: serde::de::DeserializeOwned {}
27
28    impl<T: serde::de::DeserializeOwned> Decodable for T {}
29}
30
31#[cfg(not(feature = "borsh"))]
32pub use _no_borsh::*;
33
34#[cfg(feature = "borsh")]
35mod _with_borsh {
36    pub trait Encodable: serde::ser::Serialize + borsh::BorshSerialize {}
37
38    impl<T: serde::ser::Serialize + borsh::BorshSerialize> Encodable for T {}
39
40    pub trait Decodable: serde::de::DeserializeOwned + borsh::BorshDeserialize {}
41
42    impl<T: serde::de::DeserializeOwned + borsh::BorshDeserialize> Decodable for T {}
43}
44
45#[cfg(feature = "borsh")]
46pub use _with_borsh::*;
47
48pub trait DataAnchorEncoding {
49    fn encode<T: Encodable>(&self, data: &T) -> DataAnchorEncodingResult<Vec<u8>>;
50    fn decode<T: Decodable>(&self, data: &[u8]) -> DataAnchorEncodingResult<T>;
51}
52
53#[derive(Debug, Clone, Copy, std::default::Default)]
54pub struct Postcard;
55
56pub use Postcard as Default;
57
58impl DataAnchorEncoding for Postcard {
59    fn encode<T: Encodable>(&self, data: &T) -> DataAnchorEncodingResult<Vec<u8>> {
60        Ok(postcard::to_allocvec(data)?)
61    }
62
63    fn decode<T: Decodable>(&self, data: &[u8]) -> DataAnchorEncodingResult<T> {
64        Ok(postcard::from_bytes(data)?)
65    }
66}
67
68#[derive(Debug, Clone, Copy, std::default::Default)]
69pub struct Bincode;
70
71impl DataAnchorEncoding for Bincode {
72    fn encode<T: Encodable>(&self, data: &T) -> DataAnchorEncodingResult<Vec<u8>> {
73        Ok(bincode::serialize(data)?)
74    }
75
76    fn decode<T: Decodable>(&self, data: &[u8]) -> DataAnchorEncodingResult<T> {
77        Ok(bincode::deserialize(data)?)
78    }
79}
80
81#[derive(Debug, Clone, Copy, std::default::Default)]
82pub struct Json;
83
84impl DataAnchorEncoding for Json {
85    fn encode<T: Encodable>(&self, data: &T) -> DataAnchorEncodingResult<Vec<u8>> {
86        Ok(serde_json::to_vec(data)?)
87    }
88
89    fn decode<T: Decodable>(&self, data: &[u8]) -> DataAnchorEncodingResult<T> {
90        Ok(serde_json::from_slice(data)?)
91    }
92}
93
94#[cfg(feature = "borsh")]
95#[derive(Debug, Clone, Copy, std::default::Default)]
96pub struct Borsh;
97
98#[cfg(feature = "borsh")]
99impl DataAnchorEncoding for Borsh {
100    fn encode<T: Encodable>(&self, data: &T) -> DataAnchorEncodingResult<Vec<u8>> {
101        Ok(borsh::to_vec(data)?)
102    }
103
104    fn decode<T: Decodable>(&self, data: &[u8]) -> DataAnchorEncodingResult<T> {
105        Ok(borsh::from_slice(data)?)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use rstest::rstest;
112
113    use super::*;
114
115    #[derive(
116        Debug,
117        PartialEq,
118        serde::Serialize,
119        serde::Deserialize,
120        borsh::BorshSerialize,
121        borsh::BorshDeserialize,
122    )]
123    pub struct TestStruct {
124        pub field1: String,
125        pub field2: u32,
126    }
127
128    #[rstest]
129    #[case::string("Hello, World!".to_string())]
130    #[case::bytes(vec![20, 30])]
131    #[case::tuple((1, 2, 3))]
132    #[case::bool(true)]
133    #[case::json(TestStruct {
134        field1: "Test".to_string(),
135        field2: 42,
136    })]
137    fn test_encoding<T, E>(
138        #[case] data: T,
139        #[values(Default, Postcard, Bincode, Json, Borsh)] encoding: E,
140    ) where
141        T: Encodable + Decodable + PartialEq + std::fmt::Debug,
142        E: DataAnchorEncoding,
143    {
144        let encoded = encoding.encode(&data).unwrap();
145        let decoded: T = encoding.decode(&encoded).unwrap();
146        assert_eq!(data, decoded);
147    }
148}