lurk_ipld/
codec_impl.rs

1//! IPLD Codecs.
2#[cfg(feature = "dag-cbor")]
3use crate::cbor::DagCborCodec;
4use crate::cid::Cid;
5use crate::codec::{Codec, Decode, Encode, References};
6use crate::error::{Result, UnsupportedCodec};
7use crate::ipld::Ipld;
8#[cfg(feature = "dag-json")]
9use crate::json::DagJsonCodec;
10#[cfg(feature = "dag-pb")]
11use crate::pb::DagPbCodec;
12use crate::raw::RawCodec;
13use core::convert::TryFrom;
14use std::io::{Read, Seek, Write};
15
16/// Default codecs.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum IpldCodec {
19    /// Raw codec.
20    Raw,
21    /// Cbor codec.
22    #[cfg(feature = "dag-cbor")]
23    DagCbor,
24    /// Json codec.
25    #[cfg(feature = "dag-json")]
26    DagJson,
27    /// Protobuf codec.
28    #[cfg(feature = "dag-pb")]
29    DagPb,
30}
31
32impl TryFrom<u64> for IpldCodec {
33    type Error = UnsupportedCodec;
34
35    fn try_from(ccode: u64) -> core::result::Result<Self, Self::Error> {
36        Ok(match ccode {
37            0x55 => Self::Raw,
38            #[cfg(feature = "dag-cbor")]
39            0x71 => Self::DagCbor,
40            #[cfg(feature = "dag-json")]
41            0x0129 => Self::DagJson,
42            #[cfg(feature = "dag-pb")]
43            0x70 => Self::DagPb,
44            _ => return Err(UnsupportedCodec(ccode)),
45        })
46    }
47}
48
49impl From<IpldCodec> for u64 {
50    fn from(mc: IpldCodec) -> Self {
51        match mc {
52            IpldCodec::Raw => 0x55,
53            #[cfg(feature = "dag-cbor")]
54            IpldCodec::DagCbor => 0x71,
55            #[cfg(feature = "dag-json")]
56            IpldCodec::DagJson => 0x0129,
57            #[cfg(feature = "dag-pb")]
58            IpldCodec::DagPb => 0x70,
59        }
60    }
61}
62
63impl From<RawCodec> for IpldCodec {
64    fn from(_: RawCodec) -> Self {
65        Self::Raw
66    }
67}
68
69#[cfg(feature = "dag-cbor")]
70impl From<DagCborCodec> for IpldCodec {
71    fn from(_: DagCborCodec) -> Self {
72        Self::DagCbor
73    }
74}
75
76#[cfg(feature = "dag-cbor")]
77impl From<IpldCodec> for DagCborCodec {
78    fn from(_: IpldCodec) -> Self {
79        Self
80    }
81}
82
83#[cfg(feature = "dag-json")]
84impl From<DagJsonCodec> for IpldCodec {
85    fn from(_: DagJsonCodec) -> Self {
86        Self::DagJson
87    }
88}
89
90#[cfg(feature = "dag-json")]
91impl From<IpldCodec> for DagJsonCodec {
92    fn from(_: IpldCodec) -> Self {
93        Self
94    }
95}
96
97#[cfg(feature = "dag-pb")]
98impl From<DagPbCodec> for IpldCodec {
99    fn from(_: DagPbCodec) -> Self {
100        Self::DagPb
101    }
102}
103
104#[cfg(feature = "dag-pb")]
105impl From<IpldCodec> for DagPbCodec {
106    fn from(_: IpldCodec) -> Self {
107        Self
108    }
109}
110
111impl Codec for IpldCodec {}
112
113impl Encode<IpldCodec> for Ipld {
114    fn encode<W: Write>(&self, c: IpldCodec, w: &mut W) -> Result<()> {
115        match c {
116            IpldCodec::Raw => self.encode(RawCodec, w)?,
117            #[cfg(feature = "dag-cbor")]
118            IpldCodec::DagCbor => self.encode(DagCborCodec, w)?,
119            #[cfg(feature = "dag-json")]
120            IpldCodec::DagJson => self.encode(DagJsonCodec, w)?,
121            #[cfg(feature = "dag-pb")]
122            IpldCodec::DagPb => self.encode(DagPbCodec, w)?,
123        };
124        Ok(())
125    }
126}
127
128impl Decode<IpldCodec> for Ipld {
129    fn decode<R: Read + Seek>(c: IpldCodec, r: &mut R) -> Result<Self> {
130        Ok(match c {
131            IpldCodec::Raw => Self::decode(RawCodec, r)?,
132            #[cfg(feature = "dag-cbor")]
133            IpldCodec::DagCbor => Self::decode(DagCborCodec, r)?,
134            #[cfg(feature = "dag-json")]
135            IpldCodec::DagJson => Self::decode(DagJsonCodec, r)?,
136            #[cfg(feature = "dag-pb")]
137            IpldCodec::DagPb => Self::decode(DagPbCodec, r)?,
138        })
139    }
140}
141
142impl References<IpldCodec> for Ipld {
143    fn references<R: Read + Seek, E: Extend<Cid>>(
144        c: IpldCodec,
145        r: &mut R,
146        set: &mut E,
147    ) -> Result<()> {
148        match c {
149            IpldCodec::Raw => <Self as References<RawCodec>>::references(RawCodec, r, set)?,
150            #[cfg(feature = "dag-cbor")]
151            IpldCodec::DagCbor => {
152                <Self as References<DagCborCodec>>::references(DagCborCodec, r, set)?
153            }
154            #[cfg(feature = "dag-json")]
155            IpldCodec::DagJson => {
156                <Self as References<DagJsonCodec>>::references(DagJsonCodec, r, set)?
157            }
158            #[cfg(feature = "dag-pb")]
159            IpldCodec::DagPb => <Self as References<DagPbCodec>>::references(DagPbCodec, r, set)?,
160        };
161        Ok(())
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn raw_encode() {
171        let data = Ipld::Bytes([0x22, 0x33, 0x44].to_vec());
172        let result = IpldCodec::Raw.encode(&data).unwrap();
173        assert_eq!(result, vec![0x22, 0x33, 0x44]);
174    }
175
176    #[test]
177    fn raw_decode() {
178        let data = [0x22, 0x33, 0x44];
179        let result: Ipld = IpldCodec::Raw.decode(&data).unwrap();
180        assert_eq!(result, Ipld::Bytes(data.to_vec()));
181    }
182
183    #[cfg(feature = "dag-cbor")]
184    #[test]
185    fn dag_cbor_encode() {
186        let data = Ipld::Bytes([0x22, 0x33, 0x44].to_vec());
187        let result = IpldCodec::DagCbor.encode(&data).unwrap();
188        assert_eq!(result, vec![0x43, 0x22, 0x33, 0x44]);
189    }
190
191    #[cfg(feature = "dag-cbor")]
192    #[test]
193    fn dag_cbor_decode() {
194        let data = [0x43, 0x22, 0x33, 0x44];
195        let result: Ipld = IpldCodec::DagCbor.decode(&data).unwrap();
196        assert_eq!(result, Ipld::Bytes(vec![0x22, 0x33, 0x44]));
197    }
198
199    #[cfg(feature = "dag-json")]
200    #[test]
201    fn dag_json_encode() {
202        let data = Ipld::Bool(true);
203        let result = String::from_utf8(IpldCodec::DagJson.encode(&data).unwrap().to_vec()).unwrap();
204        assert_eq!(result, "true");
205    }
206
207    #[cfg(feature = "dag-json")]
208    #[test]
209    fn dag_json_decode() {
210        let data = b"true";
211        let result: Ipld = IpldCodec::DagJson.decode(data).unwrap();
212        assert_eq!(result, Ipld::Bool(true));
213    }
214
215    #[cfg(feature = "dag-pb")]
216    #[test]
217    fn dag_pb_encode() {
218        let mut data_map = std::collections::BTreeMap::<String, Ipld>::new();
219        data_map.insert("Data".to_string(), Ipld::Bytes(b"data".to_vec()));
220        data_map.insert("Links".to_string(), Ipld::List(vec![]));
221
222        let data = Ipld::Map(data_map);
223        let result = IpldCodec::DagPb.encode(&data).unwrap();
224        assert_eq!(result, vec![0x0a, 0x04, 0x64, 0x61, 0x74, 0x61]);
225    }
226
227    #[cfg(feature = "dag-pb")]
228    #[test]
229    fn dag_pb_decode() {
230        let mut data_map = std::collections::BTreeMap::<String, Ipld>::new();
231        data_map.insert("Data".to_string(), Ipld::Bytes(b"data".to_vec()));
232        data_map.insert("Links".to_string(), Ipld::List(vec![]));
233        let expected = Ipld::Map(data_map);
234
235        let data = [0x0a, 0x04, 0x64, 0x61, 0x74, 0x61];
236        let result: Ipld = IpldCodec::DagPb.decode(&data).unwrap();
237        assert_eq!(result, expected);
238    }
239}