libipld_core/
codec.rs

1//! `Ipld` codecs.
2use alloc::{format, string::String, vec::Vec};
3use core::{convert::TryFrom, ops::Deref};
4
5use crate::cid::Cid;
6use crate::error::{Result, UnsupportedCodec};
7use crate::io::{Cursor, Read, Seek, Write};
8use crate::ipld::Ipld;
9
10/// Codec trait.
11pub trait Codec:
12    Copy + Unpin + Send + Sync + 'static + Sized + TryFrom<u64, Error = UnsupportedCodec> + Into<u64>
13{
14    /// Encodes an encodable type.
15    fn encode<T: Encode<Self> + ?Sized>(&self, obj: &T) -> Result<Vec<u8>> {
16        let mut buf = Vec::with_capacity(u16::MAX as usize);
17        obj.encode(*self, &mut buf)?;
18        Ok(buf)
19    }
20
21    /// Decodes a decodable type.
22    fn decode<T: Decode<Self>>(&self, bytes: &[u8]) -> Result<T> {
23        T::decode(*self, &mut Cursor::new(bytes))
24    }
25
26    /// Scrapes the references.
27    fn references<T: References<Self>, E: Extend<Cid>>(
28        &self,
29        bytes: &[u8],
30        set: &mut E,
31    ) -> Result<()> {
32        T::references(*self, &mut Cursor::new(bytes), set)
33    }
34}
35
36/// Encode trait.
37///
38/// This trait is generic over a codec, so that different codecs can be implemented for the same
39/// type.
40pub trait Encode<C: Codec> {
41    /// Encodes into a `impl Write`.
42    ///
43    /// It takes a specific codec as parameter, so that the [`Encode`] can be generic over an enum
44    /// that contains multiple codecs.
45    fn encode<W: Write>(&self, c: C, w: &mut W) -> Result<()>;
46}
47
48impl<C: Codec, T: Encode<C>> Encode<C> for &T {
49    fn encode<W: Write>(&self, c: C, w: &mut W) -> Result<()> {
50        self.deref().encode(c, w)
51    }
52}
53
54/// Decode trait.
55///
56/// This trait is generic over a codec, so that different codecs can be implemented for the same
57/// type.
58pub trait Decode<C: Codec>: Sized {
59    /// Decode from an `impl Read`.
60    ///
61    /// It takes a specific codec as parameter, so that the [`Decode`] can be generic over an enum
62    /// that contains multiple codecs.
63    fn decode<R: Read + Seek>(c: C, r: &mut R) -> Result<Self>;
64}
65
66/// References trait.
67///
68/// This trait is generic over a codec, so that different codecs can be implemented for the same
69/// type.
70pub trait References<C: Codec>: Sized {
71    /// Scrape the references from an `impl Read`.
72    ///
73    /// It takes a specific codec as parameter, so that the [`References`] can be generic over an
74    /// enum that contains multiple codecs.
75    fn references<R: Read + Seek, E: Extend<Cid>>(c: C, r: &mut R, set: &mut E) -> Result<()>;
76}
77
78/// Utility for testing codecs.
79///
80/// Encodes the `data` using the codec `c` and checks that it matches the `ipld`.
81pub fn assert_roundtrip<C, T>(c: C, data: &T, ipld: &Ipld)
82where
83    C: Codec,
84    T: Decode<C> + Encode<C> + core::fmt::Debug + PartialEq,
85    Ipld: Decode<C> + Encode<C>,
86{
87    fn hex(bytes: &[u8]) -> String {
88        bytes.iter().map(|byte| format!("{:02x}", byte)).collect()
89    }
90    let mut bytes = Vec::new();
91    data.encode(c, &mut bytes).unwrap();
92    let mut bytes2 = Vec::new();
93    ipld.encode(c, &mut bytes2).unwrap();
94    if bytes != bytes2 {
95        panic!(
96            r#"assertion failed: `(left == right)`
97        left: `{}`,
98       right: `{}`"#,
99            hex(&bytes),
100            hex(&bytes2)
101        );
102    }
103    let ipld2: Ipld = Decode::decode(c, &mut Cursor::new(bytes.as_slice())).unwrap();
104    assert_eq!(&ipld2, ipld);
105    let data2: T = Decode::decode(c, &mut Cursor::new(bytes.as_slice())).unwrap();
106    assert_eq!(&data2, data);
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use crate::ipld::Ipld;
113    use anyhow::anyhow;
114
115    #[derive(Clone, Copy, Debug)]
116    struct CodecImpl;
117
118    impl Codec for CodecImpl {}
119
120    impl From<CodecImpl> for u64 {
121        fn from(_: CodecImpl) -> Self {
122            0
123        }
124    }
125
126    impl TryFrom<u64> for CodecImpl {
127        type Error = UnsupportedCodec;
128
129        fn try_from(_: u64) -> core::result::Result<Self, Self::Error> {
130            Ok(Self)
131        }
132    }
133
134    impl Encode<CodecImpl> for Ipld {
135        fn encode<W: Write>(&self, _: CodecImpl, w: &mut W) -> Result<()> {
136            match self {
137                Self::Null => Ok(w.write_all(&[0]).map_err(anyhow::Error::msg)?),
138                _ => Err(anyhow!("not null")),
139            }
140        }
141    }
142
143    impl Decode<CodecImpl> for Ipld {
144        fn decode<R: Read>(_: CodecImpl, r: &mut R) -> Result<Self> {
145            let mut buf = [0; 1];
146            r.read_exact(&mut buf).map_err(anyhow::Error::msg)?;
147            if buf[0] == 0 {
148                Ok(Ipld::Null)
149            } else {
150                Err(anyhow!("not null"))
151            }
152        }
153    }
154
155    #[test]
156    fn test_codec() {
157        let bytes = CodecImpl.encode(&Ipld::Null).unwrap();
158        let ipld: Ipld = CodecImpl.decode(&bytes).unwrap();
159        assert_eq!(ipld, Ipld::Null);
160    }
161}