Skip to main content

evento_sql/
sql_types.rs

1//! SQL type wrappers for serialization.
2//!
3//! This module provides the [`Bitcode`] wrapper type for compact binary serialization
4//! of Rust types to/from SQL BLOB columns using the bitcode format.
5
6use std::ops::{Deref, DerefMut};
7
8use sqlx::database::Database;
9use sqlx::decode::Decode;
10use sqlx::encode::{Encode, IsNull};
11use sqlx::error::BoxDynError;
12use sqlx::sqlite::{SqliteArgumentValue, SqliteTypeInfo};
13use sqlx::types::Type;
14
15/// A wrapper type for bitcode-serialized data in SQL databases.
16///
17/// `Bitcode<T>` wraps a value of type `T` and provides automatic serialization/deserialization
18/// using the [bitcode](https://crates.io/crates/bitcode) binary format when storing to or
19/// reading from SQL databases.
20///
21/// # Features
22///
23/// - **Compact binary format** - Efficient storage compared to JSON or other text formats
24/// - **Type-safe** - Compile-time checking of serialization capabilities
25/// - **Fast** - High performance serialization and deserialization
26///
27/// # Database Support
28///
29/// Currently implements SQLx traits for SQLite. Data is stored as BLOB.
30///
31/// # Example
32///
33/// ```rust,ignore
34/// use evento_sql::sql_types::Bitcode;
35/// use bitcode::{Encode, Decode};
36///
37/// #[derive(Encode, Decode)]
38/// struct MyData {
39///     value: i32,
40///     name: String,
41/// }
42///
43/// // Wrap data for storage
44/// let data = Bitcode(MyData { value: 42, name: "test".into() });
45///
46/// // Encode to bytes
47/// let bytes = data.encode_to()?;
48///
49/// // Decode from bytes
50/// let decoded = Bitcode::<MyData>::decode_from_bytes(&bytes)?;
51/// assert_eq!(decoded.value, 42);
52/// ```
53///
54/// # Deref
55///
56/// `Bitcode<T>` implements `Deref` and `DerefMut` to `T`, allowing transparent access
57/// to the inner value.
58#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
59pub struct Bitcode<T: ?Sized>(pub T);
60
61impl<T> From<T> for Bitcode<T> {
62    fn from(value: T) -> Self {
63        Self(value)
64    }
65}
66
67impl<T> Deref for Bitcode<T> {
68    type Target = T;
69
70    fn deref(&self) -> &Self::Target {
71        &self.0
72    }
73}
74
75impl<T> DerefMut for Bitcode<T> {
76    fn deref_mut(&mut self) -> &mut Self::Target {
77        &mut self.0
78    }
79}
80
81impl<T> AsRef<T> for Bitcode<T> {
82    fn as_ref(&self) -> &T {
83        &self.0
84    }
85}
86
87impl<T> AsMut<T> for Bitcode<T> {
88    fn as_mut(&mut self) -> &mut T {
89        &mut self.0
90    }
91}
92
93impl<T> Bitcode<T>
94where
95    T: bitcode::Encode,
96{
97    /// Serializes the wrapped value to a byte vector using bitcode.
98    pub fn encode_to(&self) -> Vec<u8> {
99        bitcode::encode(&self.0)
100    }
101}
102
103impl<T> Bitcode<T>
104where
105    T: bitcode::DecodeOwned,
106{
107    /// Deserializes a value from a byte slice using bitcode.
108    ///
109    /// # Errors
110    ///
111    /// Returns an error if deserialization fails or the data is invalid.
112    pub fn decode_from_bytes(bytes: &[u8]) -> Result<Self, bitcode::Error> {
113        let data = bitcode::decode::<T>(bytes)?;
114        Ok(Self(data))
115    }
116}
117
118impl<T> Type<sqlx::Sqlite> for Bitcode<T> {
119    fn type_info() -> SqliteTypeInfo {
120        <&[u8] as Type<sqlx::Sqlite>>::type_info()
121    }
122
123    fn compatible(ty: &SqliteTypeInfo) -> bool {
124        <&[u8] as Type<sqlx::Sqlite>>::compatible(ty)
125    }
126}
127
128impl<T> Encode<'_, sqlx::Sqlite> for Bitcode<T>
129where
130    T: bitcode::Encode,
131{
132    fn encode_by_ref(
133        &self,
134        buf: &mut <sqlx::Sqlite as Database>::ArgumentBuffer<'_>,
135    ) -> Result<IsNull, BoxDynError> {
136        buf.push(SqliteArgumentValue::Blob(std::borrow::Cow::Owned(
137            self.encode_to(),
138        )));
139
140        Ok(IsNull::No)
141    }
142}
143
144impl<'r, T> Decode<'r, sqlx::Sqlite> for Bitcode<T>
145where
146    T: bitcode::DecodeOwned,
147    Vec<u8>: sqlx::Decode<'r, sqlx::Sqlite>,
148{
149    fn decode(value: <sqlx::Sqlite as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
150        let decoded = Vec::<u8>::decode(value)?;
151        let data = Bitcode::<T>::decode_from_bytes(&decoded[..])?;
152
153        Ok(data)
154    }
155}