native_model/
lib.rs

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