snops_common/format/
mod.rs

1use std::{
2    fmt::Display,
3    io::{Read, Write},
4};
5
6mod impl_checkpoint;
7mod impl_chrono;
8mod impl_collections;
9mod impl_containers;
10mod impl_ints;
11mod impl_json;
12mod impl_net;
13mod impl_strings;
14mod impl_tuples;
15mod packed_int;
16
17pub use packed_int::*;
18use thiserror::Error;
19
20#[derive(Debug, Error)]
21pub enum DataWriteError {
22    /// Error from writing data
23    #[error("io error: {0}")]
24    Io(#[from] std::io::Error),
25    /// A custom user defined error
26    #[error("{0}")]
27    Custom(String),
28}
29
30#[derive(Debug, Error)]
31pub enum DataReadError {
32    /// Error from reading data
33    #[error("io error: {0}")]
34    Io(#[from] std::io::Error),
35    /// Error from reading UTF-8 strings
36    #[error("utf8 error: {0}")]
37    Utf8(#[from] std::string::FromUtf8Error),
38    /// The read data cannot be automatically upgraded given the available
39    /// headers
40    #[error("upgrade unavailable: {0}")]
41    UpgradeUnavailable(String),
42    #[error("invalid version: {0}")]
43    UnsupportedVersion(String),
44    /// A custom user defined error
45    #[error("{0}")]
46    Custom(String),
47}
48
49impl DataReadError {
50    pub fn unsupported(name: impl Display, expected: impl Display, found: impl Display) -> Self {
51        Self::UnsupportedVersion(format!("{name}: expected {expected}, found {found}"))
52    }
53
54    pub fn custom(error: impl Display) -> Self {
55        Self::Custom(format!("{error}"))
56    }
57}
58
59impl DataWriteError {
60    pub fn custom(error: impl Display) -> Self {
61        Self::Custom(format!("{error}"))
62    }
63}
64
65/// Write data and its header to a writer
66pub fn write_dataformat<W: Write, F: DataFormat>(
67    writer: &mut W,
68    data: &F,
69) -> Result<usize, DataWriteError> {
70    Ok(data.write_header(writer)? + data.write_data(writer)?)
71}
72
73/// Read data and its header from a reader
74pub fn read_dataformat<R: Read, F: DataFormat>(reader: &mut R) -> Result<F, DataReadError> {
75    let header = F::read_header(reader)?;
76    F::read_data(reader, &header)
77}
78
79pub type DataHeaderOf<T> = <T as DataFormat>::Header;
80
81/// `DataFormat` is a trait for serializing and deserializing binary data.
82///
83/// A header is read/written containing the versions of the desired data
84pub trait DataFormat: Sized {
85    type Header: DataFormat + Clone;
86    const LATEST_HEADER: Self::Header;
87
88    fn write_header<W: Write>(&self, writer: &mut W) -> Result<usize, DataWriteError> {
89        Ok(Self::LATEST_HEADER.write_header(writer)? + Self::LATEST_HEADER.write_data(writer)?)
90    }
91
92    fn read_header<R: Read>(reader: &mut R) -> Result<Self::Header, DataReadError> {
93        // read the header's header
94        let header_header = Self::Header::read_header(reader)?;
95        // read the header's data
96        reader.read_data(&header_header)
97    }
98
99    /// Write the data to the writer
100    fn write_data<W: Write>(&self, writer: &mut W) -> Result<usize, DataWriteError>;
101
102    /// Read the data from the reader
103    fn read_data<R: Read>(reader: &mut R, header: &Self::Header) -> Result<Self, DataReadError>;
104
105    /// Convert the data to a byte vector
106    fn to_byte_vec(&self) -> Result<Vec<u8>, DataWriteError> {
107        let mut buf = Vec::new();
108        self.write_data(&mut buf)?;
109        Ok(buf)
110    }
111}
112
113pub trait DataFormatWriter {
114    fn write_data<F: DataFormat>(&mut self, data: &F) -> Result<usize, DataWriteError>;
115}
116
117impl<W: Write> DataFormatWriter for W {
118    fn write_data<F: DataFormat>(&mut self, data: &F) -> Result<usize, DataWriteError> {
119        data.write_data(self)
120    }
121}
122
123pub trait DataFormatReader {
124    fn read_data<F: DataFormat>(&mut self, header: &F::Header) -> Result<F, DataReadError>;
125}
126
127impl<R: Read> DataFormatReader for R {
128    fn read_data<F: DataFormat>(&mut self, header: &F::Header) -> Result<F, DataReadError> {
129        F::read_data(self, header)
130    }
131}
132
133#[macro_export]
134macro_rules! dataformat_test {
135    ($name:ident, $( $others:expr),* ) => {
136        #[test]
137        fn $name() -> Result<(), Box<dyn std::error::Error>> {
138            use snops_common::format::{write_dataformat, read_dataformat};
139
140            $(
141
142                let value = $others;
143                let mut writer = Vec::new();
144                write_dataformat(&mut writer, &value)?;
145                let mut reader = writer.as_slice();
146                let decoded = read_dataformat::<_, _>(&mut reader)?;
147                assert_eq!(value, decoded);
148            )*
149            Ok(())
150        }
151    };
152}
153
154#[cfg(test)]
155mod test {
156    use super::{read_dataformat, write_dataformat, DataFormat, DataReadError, DataWriteError};
157
158    #[test]
159    fn test_read_write() -> Result<(), Box<dyn std::error::Error>> {
160        #[derive(Debug, PartialEq)]
161        struct Test {
162            a: u8,
163        }
164
165        impl DataFormat for Test {
166            type Header = u8;
167            const LATEST_HEADER: Self::Header = 1;
168
169            fn write_data<W: std::io::prelude::Write>(
170                &self,
171                writer: &mut W,
172            ) -> Result<usize, DataWriteError> {
173                self.a.write_data(writer)
174            }
175
176            fn read_data<R: std::io::prelude::Read>(
177                reader: &mut R,
178                header: &Self::Header,
179            ) -> Result<Self, DataReadError> {
180                if *header != Self::LATEST_HEADER {
181                    return Err(DataReadError::unsupported(
182                        "Test",
183                        Self::LATEST_HEADER,
184                        *header,
185                    ));
186                }
187                Ok(Test {
188                    a: u8::read_data(reader, &())?,
189                })
190            }
191        }
192
193        let value = Test { a: 42 };
194        let mut writer = Vec::new();
195
196        // two bytes are written because the header is 1 byte and the content is 1 byte
197        assert_eq!(write_dataformat(&mut writer, &value)?, 2);
198        assert_eq!(writer, [1u8, 42u8]);
199
200        let mut reader = writer.as_slice();
201        let decoded = read_dataformat::<_, Test>(&mut reader)?;
202        assert_eq!(value, decoded);
203
204        assert_eq!(
205            read_dataformat::<_, Test>(&mut [1u8, 43u8].as_slice())?,
206            Test { a: 43 }
207        );
208
209        assert!(
210            // invalid version
211            read_dataformat::<_, Test>(&mut [2u8, 43u8].as_slice()).is_err(),
212        );
213
214        assert!(
215            // EOF
216            read_dataformat::<_, Test>(&mut [1u8].as_slice()).is_err(),
217        );
218
219        Ok(())
220    }
221}