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}