native_model/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! `native_model` is a Rust crate that acts as a thin wrapper around serialized data, adding identity and version information.
//!
//! - It aims to ensure:
//! - **Interoperability**: Different applications can work together even if they use different data model versions.
//! - **Data Consistency**: Ensures the data is processed as expected.
//! - **Flexibility**: Allows the use of any serialization format. Mode details [here](https://github.com/vincent-herlemont/native_model#setup-your-serialization-format).
//! - **Minimal Performance Overhead**: Current performance has a minimal overhead see [performance](https://github.com/vincent-herlemont/native_model#performance) section.
//! - **Suitability**:
//! - Suitable for applications that are written in Rust, evolve independently, store data locally, and require incremental upgrades.
//! - Not suitable for non-Rust applications, systems not controlled by the user, or when human-readable formats are needed.
//! - **Setup**:
//! - Users must define their own serialization format and data model. Mode details [here](https://github.com/vincent-herlemont/native_model#setup-your-serialization-format).
//! - **Development Stage**:
//! - The crate is in early development, and performance is expected to improve over time.
//!
//! See examples in the [README.md](https://github.com/vincent-herlemont/native_model) file.
#[cfg(any(
feature = "serde",
feature = "bincode_1_3",
feature = "bincode_2_rc",
feature = "postcard_1_0",
feature = "rmp_serde_1_3",
doc
))]
mod codec;
#[cfg(any(
feature = "serde",
feature = "bincode_1_3",
feature = "bincode_2_rc",
feature = "postcard_1_0",
feature = "rmp_serde_1_3",
doc
))]
pub use codec::*;
mod header;
pub mod wrapper;
// Macro to generate a [`native_model`] implementation for a struct.
pub use native_model_macro::*;
use wrapper::*;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("Invalid header")]
InvalidHeader,
#[error("Failed to decode native model")]
DecodeError,
#[error(transparent)]
DecodeBodyError(#[from] DecodeBodyError),
#[error(transparent)]
EncodeBodyError(#[from] EncodeBodyError),
#[error(transparent)]
UpgradeError(#[from] UpgradeError),
#[error("Upgrade from {} to {} is not supported", from, to)]
UpgradeNotSupported { from: u32, to: u32 },
#[error(transparent)]
DowngradeError(#[from] DowngradeError),
#[error("Downgrade from {} to {} is not supported", from, to)]
DowngradeNotSupported { from: u32, to: u32 },
#[error("Wrong type id expected: {}, actual: {}", expected, actual)]
WrongTypeId { expected: u32, actual: u32 },
}
pub type DecodeResult<T> = std::result::Result<T, DecodeBodyError>;
#[derive(Error, Debug)]
#[error("Decode body error: {msg}")]
pub enum DecodeBodyError {
#[error("Mismatched model id")]
MismatchedModelId,
#[error("Decode error: {msg}")]
DecodeError {
msg: String,
#[source]
source: anyhow::Error,
},
}
pub type EncodeResult<T> = std::result::Result<T, EncodeBodyError>;
#[derive(Error, Debug)]
#[error("Encode body error: {msg}")]
pub struct EncodeBodyError {
pub msg: String,
#[source]
pub source: anyhow::Error,
}
#[derive(Error, Debug)]
#[error("Upgrade error: {msg}")]
pub struct UpgradeError {
pub msg: String,
#[source]
pub source: anyhow::Error,
}
#[derive(Error, Debug)]
#[error("Downgrade error: {msg}")]
pub struct DowngradeError {
pub msg: String,
#[source]
pub source: anyhow::Error,
}
/// Allows to encode a [`native_model`] into a [`Vec<u8>`].
///
/// See examples:
/// - [README.md](https://github.com/vincent-herlemont/native_model) file.
/// - other [examples](https://github.com/vincent-herlemont/native_model/tree/master/tests/example)
///
/// # Errors
///
/// The errors returned from this function depend on the [`Encode`] trait
/// implementor (the serializer), i.e. `bincode_2_rc`.
pub fn encode<T: crate::Model>(model: &T) -> Result<Vec<u8>> {
T::native_model_encode(model)
}
/// Allows to encode a [`native_model`] into a [`Vec<u8>`] with a specific version.
/// See examples:
/// - [README.md](https://github.com/vincent-herlemont/native_model) file.
/// - other [examples](https://github.com/vincent-herlemont/native_model/tree/master/tests/example)
pub fn encode_downgrade<T: crate::Model>(model: T, version: u32) -> Result<Vec<u8>> {
T::native_model_encode_downgrade(model, version)
}
/// Allows to decode a [`native_model`] from a [`Vec<u8>`] and returns the version ([`u32`]).
/// See examples:
/// - [README.md](https://github.com/vincent-herlemont/native_model) file.
/// - other [examples](https://github.com/vincent-herlemont/native_model/tree/master/tests/example)
///
/// # Errors
///
/// The errors returned from this function depend on the [`Decode`] trait
/// implementor (the deserializer), i.e. `bincode_2_rc`.
pub fn decode<T: crate::Model>(data: Vec<u8>) -> Result<(T, u32)> {
T::native_model_decode(data)
}
pub trait Model: Sized {
fn native_model_id() -> u32;
fn native_model_id_str() -> &'static str;
fn native_model_version() -> u32;
fn native_model_version_str() -> &'static str;
// --------------- Decode ---------------
fn native_model_decode_body(data: Vec<u8>, id: u32) -> DecodeResult<Self>;
fn native_model_decode_upgrade_body(data: Vec<u8>, id: u32, version: u32) -> Result<Self>;
fn native_model_decode(data: Vec<u8>) -> Result<(Self, u32)> {
let native_model = crate::Wrapper::deserialize(&data[..]).unwrap();
let source_id = native_model.get_id();
let source_version = native_model.get_version();
let result = Self::native_model_decode_upgrade_body(
native_model.value().to_vec(),
source_id,
source_version,
)?;
Ok((result, source_version))
}
// --------------- Encode ---------------
fn native_model_encode_body(&self) -> EncodeResult<Vec<u8>>;
fn native_model_encode_downgrade_body(self, version: u32) -> Result<Vec<u8>>;
fn native_model_encode(&self) -> Result<Vec<u8>> {
let mut data = self.native_model_encode_body()?;
let data = crate::native_model_encode(
&mut data,
Self::native_model_id(),
Self::native_model_version(),
);
Ok(data)
}
fn native_model_encode_downgrade(self, version: u32) -> Result<Vec<u8>> {
let mut data = self.native_model_encode_downgrade_body(version)?;
let data = crate::native_model_encode(&mut data, Self::native_model_id(), version);
Ok(data)
}
}