versionneer/
lib.rs

1//! This crate is a helper for versioning encoded data.
2//! It provides the `Version` trait that defines the version of data and it's encoding and decoding.
3//!
4//! ```rust
5//!    use versionneer::{versioned, Encodable, Decodable, bincode};
6//!
7//!    #[derive(Debug, thiserror::Error)]
8//!    enum Error {
9//!        #[error("Invalid version: {0}")]
10//!        InvalidVersion(u32),
11//!        #[error(transparent)]
12//!        Decode(#[from] ::bincode::error::DecodeError),
13//!        #[error(transparent)]
14//!        Encoder(#[from] ::bincode::error::EncodeError),
15//!    }
16//!    impl versionneer::Error for Error {
17//!        fn invalid_version(version: u32) -> Self {
18//!            Self::InvalidVersion(version)
19//!        }
20//!    }
21//!
22//!    #[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
23//!    struct TestV0 {
24//!        data: u8,
25//!    }
26//!    versioned!(TestV0, 0);
27//!
28//!    let mut data = Vec::new();
29//!    let mut enc = bincode::Encoder::new(&mut data);
30//!    let test = TestV0 { data: 42 };
31//!    <TestV0 as Encodable<_, Error>>::encode(&test, &mut enc).expect("Failed to encode");
32//!    let mut reader = data.as_slice();
33//!    let mut dec = bincode::Decoder::new(&mut reader);
34//!    let decoded = <TestV0 as Decodable<_, Error>>::decode(&mut dec).expect("Failed to decode");
35//!    assert_eq!(test, decoded);
36//! ```
37//!
38//! In addition it provides the `Upgrade` struct that can be used to create a upgrade chain for versioned data.
39//!
40//! ```rust
41//!    use versionneer::{versioned, Encodable, Decodable, bincode, Upgrade};
42//!
43//!    #[derive(Debug, thiserror::Error)]
44//!    enum Error {
45//!        #[error("Invalid version: {0}")]
46//!        InvalidVersion(u32),
47//!        #[error(transparent)]
48//!        Decode(#[from] ::bincode::error::DecodeError),
49//!        #[error(transparent)]
50//!        Encoder(#[from] ::bincode::error::EncodeError),
51//!    }
52//!    impl versionneer::Error for Error {
53//!        fn invalid_version(version: u32) -> Self {
54//!            Self::InvalidVersion(version)
55//!        }
56//!    }
57//!
58//!    #[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
59//!    struct TestV0 {
60//!        data: u8,
61//!    }
62//!    versioned!(TestV0, 0);
63//!
64//!    #[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
65//!    struct TestV1 {
66//!        data: u16,
67//!    }
68//!    versioned!(TestV1, 1);
69//!
70//!    impl TryFrom<TestV0> for TestV1 {
71//!        type Error = Error;
72//!        fn try_from(value: TestV0) -> Result<Self, Self::Error> {
73//!            Ok(TestV1 { data: u16::from(value.data) })
74//!        }
75//!    }
76//!
77//!    type Latest = Upgrade<TestV1, TestV0, Error>;
78//!    let mut data = Vec::new();
79//!    let mut enc = bincode::Encoder::new(&mut data);
80//!    let test = TestV0 { data: 42 };
81//!    <TestV0 as Encodable<_, Error>>::encode(&test, &mut enc).expect("Failed to encode");
82//!    let mut reader = data.as_slice();
83//!    let mut dec = bincode::Decoder::new(&mut reader);
84//!    let decoded = Latest::decode(&mut dec).expect("Failed to decode");
85//!    assert_eq!(decoded, TestV1 { data: 42 });
86//! ```
87
88#![deny(
89    warnings,
90    clippy::unwrap_used,
91    clippy::unnecessary_unwrap,
92    clippy::pedantic,
93    missing_docs
94)]
95
96#[cfg(feature = "bincode")]
97/// Bincode encoding and decoding for versionneer
98pub mod bincode;
99
100/// Versioning error trait
101pub trait Error: std::error::Error {
102    /// Create a new error for an invalid version.
103    fn invalid_version(version: u32) -> Self;
104}
105
106/// A decoder for versionneer it is used to both encode the data as well as the version.
107pub trait Decoder {
108    /// The error type for the decoder.
109    type Error: std::error::Error;
110    /// Decodes the version from the decoder.
111    ///
112    /// # Errors
113    /// This function will return an error if the decoder fails to decode the version.
114    fn decode_version(&mut self) -> Result<u32, Self::Error>;
115}
116
117/// Decode trait for versioned data, it is used in combination with the `Decoder` trait.
118///
119/// Many decoders will provide a blanket implementation for this trait.
120pub trait Decode<D: Decoder>
121where
122    Self: Sized,
123{
124    /// Decode the data from the decoder.
125    ///
126    /// # Errors
127    /// This function will return an error if the data cannot be decoded.
128    fn decode_data(decoder: &mut D) -> Result<Self, D::Error>;
129}
130
131/// The `Encoder` trait is used to encode versioned data.
132pub trait Encoder {
133    /// The error type for the encoder.
134    type Error: std::error::Error;
135    /// Encodes the version to the encoder.
136    ///
137    /// # Errors
138    /// This function will return an error if encoder fails to encode the version.
139    fn encode_version(&mut self, version: u32) -> Result<(), Self::Error>;
140}
141
142/// The `Encode` trait is used to encode versioned data.
143///
144/// Many `Encoder`s will provide a blanket implementation for this trait.
145pub trait Encode<E: Encoder>
146where
147    Self: Sized,
148{
149    /// Encodes the data to the encoder.
150    ///
151    /// # Errors
152    /// This function will return an error if encoder fails to encode the data.
153    fn encode_data(&self, encoder: &mut E) -> Result<(), E::Error>;
154}
155
156/// Versioned trait
157/// This trait is meant to be implemented either by the struct itself or by a version container.
158///
159/// The default methods do not need to be changed unelss you want to customize the behaviour of
160/// how versions are treated.
161///
162/// `E` defines the error type for the versioning process and encoding and decoding.
163///
164/// `Output` defines the type of the data that is being versioned.
165///
166/// `Version` defines the version of the data that is being versioned.
167pub trait Versioned<Err: Error>: Sized {
168    /// The type that the versioned data is encoding
169    type Output;
170    /// The version tag of this data
171    const VERSION: u32;
172}
173
174/// This trait is used to mark a versioned type as encodable. Usually this is provided by the `versioned!` macro.
175pub trait Encodable<Enc, Err>: Versioned<Err>
176where
177    Err: Error + From<Enc::Error>,
178    Enc: Encoder,
179    Self::Output: Encode<Enc>,
180{
181    /// Encodes the data including the version tag.
182    ///
183    /// # Errors
184    /// - Returns an error if the data failes to encode.
185    fn encode(data: &Self::Output, encoder: &mut Enc) -> Result<(), Err> {
186        encoder.encode_version(Self::VERSION)?;
187        data.encode_data(encoder)?;
188        Ok(())
189    }
190}
191
192/// This trait is used to mark a versioned type as decodable. Usually this is provided by the `versioned!` macro.
193pub trait Decodable<Dec, Err>: Versioned<Err>
194where
195    Err: Error + From<Dec::Error>,
196    Dec: Decoder,
197    Self::Output: Decode<Dec>,
198{
199    /// Decodes versioned data and validates the version. Will consume the version from the reader.
200    /// # Errors
201    /// - Returns an error if the version is invalid.
202    /// - Returns an error if the data is invalid.
203    fn decode(decoder: &mut Dec) -> Result<Self::Output, Err> {
204        let version: u32 = decoder.decode_version()?;
205        Self::decode_with_version(decoder, version)
206    }
207
208    /// Decodes the data with a provided version tag. Is helpful for use in combination with `Upgrade`.
209    ///
210    /// # Errors
211    /// - Returns an error if the version is invalid.
212    /// - Returns an error if the data is invalid.
213    #[inline]
214    fn decode_with_version(decoder: &mut Dec, version: u32) -> Result<Self::Output, Err> {
215        if version == Self::VERSION {
216            // We have validated the version, so we can safely decode the body.
217            unsafe { Self::decode_body(decoder) }
218        } else {
219            Err(Err::invalid_version(version))
220        }
221    }
222    /// Decodes only the body, assuming the version has already been validated.
223    ///
224    /// # Safety
225    /// This function is marked unsafe because it does not validate the version and when
226    /// used without care might lead to data corruption or other issues.
227    ///
228    /// # Errors
229    /// - Returns an error if the data is invalid.
230    #[inline]
231    unsafe fn decode_body(decoder: &mut Dec) -> Result<Self::Output, Err> {
232        let data = Self::Output::decode_data(decoder)?;
233        Ok(data)
234    }
235}
236
237/// The `Upgrade` struct is used to upgrade data from a previous version to the latest version.
238///
239/// `Prior::VERSION` must be less than `Latest::VERSION`!
240///
241/// Upgrades can be chained!
242pub struct Upgrade<Latest, Prior, Err>
243where
244    Err: Error,
245    Prior: Versioned<Err>,
246    Latest: Versioned<Err>,
247    Latest::Output: TryFrom<Prior::Output>,
248{
249    _marker: std::marker::PhantomData<(Prior, Latest, Err)>,
250}
251
252impl<Latest, Prior, Err> Versioned<Err> for Upgrade<Latest, Prior, Err>
253where
254    Err: Error,
255    Prior: Versioned<Err>,
256    Latest: Versioned<Err>,
257    Latest::Output: TryFrom<Prior::Output>,
258{
259    type Output = Latest::Output;
260    const VERSION: u32 = Latest::VERSION;
261}
262
263impl<Latest, Prior, Enc, Err> Encodable<Enc, Err> for Upgrade<Latest, Prior, Err>
264where
265    Err: Error + From<<Enc as Encoder>::Error>,
266    Enc: Encoder,
267    Prior: Versioned<Err>,
268    Latest: Versioned<Err>,
269    Latest::Output: Encode<Enc>,
270    Latest::Output: TryFrom<Prior::Output>,
271{
272}
273
274impl<Latest, Prior, Dec, Err> Decodable<Dec, Err> for Upgrade<Latest, Prior, Err>
275where
276    Dec: Decoder,
277    Prior: Versioned<Err> + Decodable<Dec, Err>,
278    Prior::Output: Decode<Dec>,
279    Latest: Versioned<Err> + Decodable<Dec, Err>,
280    Latest::Output: TryFrom<Prior::Output> + Decode<Dec>,
281    Err: Error
282        + From<<Dec as Decoder>::Error>
283        + From<<Latest::Output as TryFrom<Prior::Output>>::Error>,
284{
285    fn decode_with_version(decoder: &mut Dec, version: u32) -> Result<Self::Output, Err> {
286        debug_assert!(
287            Prior::VERSION < Latest::VERSION,
288            "Upgrade versions need to increase! Prior::Version({}) >= Latest::Version({})",
289            Prior::VERSION,
290            Latest::VERSION
291        );
292        if version == Latest::VERSION {
293            Latest::decode_with_version(decoder, version)
294        } else {
295            let prior = Prior::decode_with_version(decoder, version)?;
296            Ok(Latest::Output::try_from(prior)?)
297        }
298    }
299}
300
301/// This macro is a shortcut to crate a versioned type with the associated `Encodable` and `Decodable` traits.
302#[macro_export]
303macro_rules! versioned {
304    ($type:ty, $ver:expr) => {
305        impl<Err> $crate::Versioned<Err> for $type
306        where
307            Err: $crate::Error,
308        {
309            type Output = $type;
310            const VERSION: u32 = $ver;
311        }
312
313        impl<Enc, Err> $crate::Encodable<Enc, Err> for $type
314        where
315            Err: $crate::Error + From<<Enc as $crate::Encoder>::Error>,
316            Enc: $crate::Encoder,
317            <Self as $crate::Versioned<Err>>::Output: $crate::Encode<Enc>,
318        {
319        }
320
321        impl<Dec, Err> $crate::Decodable<Dec, Err> for $type
322        where
323            Err: $crate::Error + From<<Dec as $crate::Decoder>::Error>,
324            Dec: $crate::Decoder,
325            <Self as $crate::Versioned<Err>>::Output: $crate::Decode<Dec>,
326        {
327        }
328    };
329}