Skip to content

Instantly share code, notes, and snippets.

@alex-shapiro
Last active August 14, 2017 17:05
Show Gist options
  • Save alex-shapiro/674bc09c8d4ef133863b17d3727aad31 to your computer and use it in GitHub Desktop.
Save alex-shapiro/674bc09c8d4ef133863b17d3727aad31 to your computer and use it in GitHub Desktop.
custom Diesel type for MsgPack-encodable values
//! 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