Last active
August 14, 2017 17:05
-
-
Save alex-shapiro/674bc09c8d4ef133863b17d3727aad31 to your computer and use it in GitHub Desktop.
custom Diesel type for MsgPack-encodable values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Custom Diesel type for mapping MsgPack-encodable values to | |
//! a binary row in an SQLite database. | |
use diesel::backend::Backend; | |
use diesel::expression::AsExpression; | |
use diesel::expression::NonAggregate; | |
use diesel::expression::bound::Bound; | |
use diesel::Queryable; | |
use diesel::row::Row; | |
use diesel::sqlite::Sqlite; | |
use diesel::types::{FromSqlRow, Binary, Nullable, ToSql, FromSql, IsNull, ToSqlOutput, NotNull, SingleValue}; | |
use rmp_serde; | |
use serde::de::DeserializeOwned; | |
use serde::Serialize; | |
use std::error::Error; | |
use std::fmt::Debug; | |
use std::io::Write; | |
#[derive(Debug, Clone)] | |
pub struct MsgPack<T>(pub T); | |
impl<T> NotNull for MsgPack<T> {} | |
impl<T> SingleValue for MsgPack<T> {} | |
impl<T> NonAggregate for MsgPack<T> {} | |
impl<T: Serialize + DeserializeOwned> Queryable<Binary, Sqlite> for MsgPack<T> { | |
type Row = Self; | |
fn build(row: Self) -> Self { | |
row | |
} | |
} | |
impl<T> AsExpression<Binary> for MsgPack<T> { | |
type Expression = Bound<Binary, MsgPack<T>>; | |
fn as_expression(self) -> Self::Expression { | |
Bound::new(self) | |
} | |
} | |
impl<'a, T> AsExpression<Binary> for &'a MsgPack<T> { | |
type Expression = Bound<Binary, &'a MsgPack<T>>; | |
fn as_expression(self) -> Self::Expression { | |
Bound::new(self) | |
} | |
} | |
impl<T> AsExpression<Nullable<Binary>> for MsgPack<T> { | |
type Expression = Bound<Nullable<Binary>, MsgPack<T>>; | |
fn as_expression(self) -> Self::Expression { | |
Bound::new(self) | |
} | |
} | |
impl<'a, T: Serialize> AsExpression<Nullable<Binary>> for &'a MsgPack<T> { | |
type Expression = Bound<Nullable<Binary>, &'a MsgPack<T>>; | |
fn as_expression(self) -> Self::Expression { | |
Bound::new(self) | |
} | |
} | |
impl<T: Serialize + DeserializeOwned> FromSqlRow<Binary, Sqlite> for MsgPack<T> { | |
fn build_from_row<R: Row<Sqlite>>(row: &mut R) -> Result<Self, Box<Error + Send + Sync>> { | |
FromSql::<Binary, Sqlite>::from_sql(row.take()) | |
} | |
} | |
impl<T: Serialize + DeserializeOwned + Debug> ToSql<Binary, Sqlite> for MsgPack<T> { | |
fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Sqlite>) -> Result<IsNull, Box<Error + Send + Sync>>{ | |
let vec = rmp_serde::to_vec(&self.0).unwrap(); | |
ToSql::<Binary, Sqlite>::to_sql(&vec, out) | |
} | |
} | |
impl<T: Serialize + DeserializeOwned> FromSql<Binary, Sqlite> for MsgPack<T> { | |
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> ::std::result::Result<Self, Box<Error + Send + Sync>>{ | |
if let Some(bytes) = bytes { | |
let ref slice = bytes.read_blob(); | |
Ok(MsgPack(rmp_serde::from_slice(&slice)?)) | |
} else { | |
Err(Box::new(::diesel::types::impls::option::UnexpectedNullError { | |
msg: "Unexpected null for non-null column".to_string(), | |
})) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment