#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
warnings,
clippy::unwrap_used,
clippy::unnecessary_unwrap,
clippy::pedantic,
missing_docs
)]
#[cfg_attr(
feature = "bincode",
doc = r#"
```rust
use versionneer::{versioned, Encodable, Decodable, bincode};
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("Invalid version: {0}")]
InvalidVersion(u32),
#[error(transparent)]
Decode(#[from] ::bincode::error::DecodeError),
#[error(transparent)]
Encoder(#[from] ::bincode::error::EncodeError),
}
impl versionneer::Error for Error {
fn invalid_version(version: u32) -> Self {
Self::InvalidVersion(version)
}
}
#[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
struct TestV0 {
data: u8,
}
versioned!(TestV0, 0, Error);
let mut data = Vec::new();
let mut enc = bincode::Encoder::new(&mut data);
let test = TestV0 { data: 42 };
TestV0::encode(&test, &mut enc).expect("Failed to encode");
let mut reader = data.as_slice();
let mut dec = bincode::Decoder::new(&mut reader);
let decoded = TestV0::decode(&mut dec).expect("Failed to decode");
assert_eq!(test, decoded);
```
In addition it provides the `Upgrade` struct that can be used to create a upgrade chain for versioned data.
```rust
use versionneer::{versioned, Encodable, Decodable, bincode, Upgrade};
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("Invalid version: {0}")]
InvalidVersion(u32),
#[error(transparent)]
Decode(#[from] ::bincode::error::DecodeError),
#[error(transparent)]
Encoder(#[from] ::bincode::error::EncodeError),
}
impl versionneer::Error for Error {
fn invalid_version(version: u32) -> Self {
Self::InvalidVersion(version)
}
}
#[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
struct TestV0 {
data: u8,
}
versioned!(TestV0, 0, Error);
#[derive(Debug, PartialEq, Eq, ::bincode::Decode, ::bincode::Encode)]
struct TestV1 {
data: u16,
}
versioned!(TestV1, 1, Error);
impl TryFrom<TestV0> for TestV1 {
type Error = Error;
fn try_from(value: TestV0) -> Result<Self, Self::Error> {
Ok(TestV1 { data: u16::from(value.data) })
}
}
type Latest = Upgrade<TestV1, TestV0, Error>;
let mut data = Vec::new();
let mut enc = bincode::Encoder::new(&mut data);
let test = TestV0 { data: 42 };
TestV0::encode(&test, &mut enc).expect("Failed to encode");
let mut reader = data.as_slice();
let mut dec = bincode::Decoder::new(&mut reader);
let decoded = Latest::decode(&mut dec).expect("Failed to decode");
assert_eq!(decoded, TestV1 { data: 42 });
```
"#
)]
#[cfg(feature = "bincode")]
pub mod bincode;
#[cfg(feature = "simd-json")]
pub mod simd_json;
pub trait Error: core::error::Error {
fn invalid_version(version: u32) -> Self;
}
pub trait Decoder {
type Error: core::error::Error;
fn decode_version(&mut self) -> Result<u32, Self::Error>;
}
pub trait Decode<D: Decoder>
where
Self: Sized,
{
fn decode_data(decoder: &mut D) -> Result<Self, D::Error>;
}
pub trait Encoder {
type Error: core::error::Error;
fn encode_version(&mut self, version: u32) -> Result<(), Self::Error>;
}
pub trait Encode<E: Encoder>
where
Self: Sized,
{
fn encode_data(&self, encoder: &mut E) -> Result<(), E::Error>;
}
pub trait Versioned<Err: Error>: Sized {
type Output;
const VERSION: u32;
}
pub trait Encodable<Enc, Err>: Versioned<Err>
where
Err: Error + From<Enc::Error>,
Enc: Encoder,
Self::Output: Encode<Enc>,
{
fn encode(data: &Self::Output, encoder: &mut Enc) -> Result<(), Err> {
encoder.encode_version(Self::VERSION)?;
data.encode_data(encoder)?;
Ok(())
}
}
pub trait Decodable<Dec, Err>: Versioned<Err>
where
Err: Error + From<Dec::Error>,
Dec: Decoder,
Self::Output: Decode<Dec>,
{
fn decode(decoder: &mut Dec) -> Result<Self::Output, Err> {
let version: u32 = decoder.decode_version()?;
Self::decode_with_version(decoder, version)
}
#[inline]
fn decode_with_version(decoder: &mut Dec, version: u32) -> Result<Self::Output, Err> {
if version == Self::VERSION {
unsafe { Self::decode_body(decoder) }
} else {
Err(Err::invalid_version(version))
}
}
#[inline]
unsafe fn decode_body(decoder: &mut Dec) -> Result<Self::Output, Err> {
let data = Self::Output::decode_data(decoder)?;
Ok(data)
}
}
pub struct Upgrade<Latest, Prior, Err>
where
Err: Error,
Prior: Versioned<Err>,
Latest: Versioned<Err>,
Latest::Output: TryFrom<Prior::Output>,
{
_marker: core::marker::PhantomData<(Prior, Latest, Err)>,
}
impl<Latest, Prior, Err> Versioned<Err> for Upgrade<Latest, Prior, Err>
where
Err: Error,
Prior: Versioned<Err>,
Latest: Versioned<Err>,
Latest::Output: TryFrom<Prior::Output>,
{
type Output = Latest::Output;
const VERSION: u32 = Latest::VERSION;
}
impl<Latest, Prior, Enc, Err> Encodable<Enc, Err> for Upgrade<Latest, Prior, Err>
where
Err: Error + From<<Enc as Encoder>::Error>,
Enc: Encoder,
Prior: Versioned<Err>,
Latest: Versioned<Err>,
Latest::Output: Encode<Enc>,
Latest::Output: TryFrom<Prior::Output>,
{
}
impl<Latest, Prior, Dec, Err> Decodable<Dec, Err> for Upgrade<Latest, Prior, Err>
where
Dec: Decoder,
Prior: Versioned<Err> + Decodable<Dec, Err>,
Prior::Output: Decode<Dec>,
Latest: Versioned<Err> + Decodable<Dec, Err>,
Latest::Output: TryFrom<Prior::Output> + Decode<Dec>,
Err: Error
+ From<<Dec as Decoder>::Error>
+ From<<Latest::Output as TryFrom<Prior::Output>>::Error>,
{
fn decode_with_version(decoder: &mut Dec, version: u32) -> Result<Self::Output, Err> {
debug_assert!(
Prior::VERSION < Latest::VERSION,
"Upgrade versions need to increase! Prior::Version({}) >= Latest::Version({})",
Prior::VERSION,
Latest::VERSION
);
if version == Latest::VERSION {
Latest::decode_with_version(decoder, version)
} else {
let prior = Prior::decode_with_version(decoder, version)?;
Ok(Latest::Output::try_from(prior)?)
}
}
}
#[macro_export]
macro_rules! versioned {
($type:ty, $ver:expr, $err:ty) => {
impl $crate::Versioned<$err> for $type {
type Output = $type;
const VERSION: u32 = $ver;
}
impl<Enc> $crate::Encodable<Enc, $err> for $type
where
Enc: $crate::Encoder,
$err: From<<Enc as $crate::Encoder>::Error>,
<Self as $crate::Versioned<$err>>::Output: $crate::Encode<Enc>,
{
}
impl<Dec> $crate::Decodable<Dec, $err> for $type
where
Dec: $crate::Decoder,
$err: From<<Dec as $crate::Decoder>::Error>,
<Self as $crate::Versioned<$err>>::Output: $crate::Decode<Dec>,
{
}
};
($type:ty, $ver:expr) => {
impl<Err> $crate::Versioned<Err> for $type
where
Err: $crate::Error,
{
type Output = $type;
const VERSION: u32 = $ver;
}
impl<Enc, Err> $crate::Encodable<Enc, Err> for $type
where
Err: $crate::Error + From<<Enc as $crate::Encoder>::Error>,
Enc: $crate::Encoder,
<Self as $crate::Versioned<Err>>::Output: $crate::Encode<Enc>,
{
}
impl<Dec, Err> $crate::Decodable<Dec, Err> for $type
where
Err: $crate::Error + From<<Dec as $crate::Decoder>::Error>,
Dec: $crate::Decoder,
<Self as $crate::Versioned<Err>>::Output: $crate::Decode<Dec>,
{
}
};
}