-
-
Save durango/0e7e002407c41d01bdc7a32f9385c439 to your computer and use it in GitHub Desktop.
Custom Diesel wrapper type example
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
/// This defines a custom Diesel type for storing a 6-bit integer and a 58-bit | |
/// integer in the same 64-bit SQL column | |
use std::error::Error; | |
use std::fmt; | |
use std::io::Write; | |
use diesel::backend::Backend; | |
use diesel::types::{FromSqlRow,FromSql,ToSql,HasSqlType,IsNull,BigInt,Nullable}; | |
use diesel::expression::AsExpression; | |
use diesel::row::Row; | |
// store depth in 6 bits + store index in MAX_DEPTH bits = 64 | |
const COORDINATE_MAX_DEPTH: usize = 58; | |
const COORDINATE_INDEX_MASK: u64 = (1u64<<COORDINATE_MAX_DEPTH)-1; | |
#[derive(Debug)] | |
enum CoordinateError { | |
From(i64), | |
To(Coordinate), | |
} | |
impl fmt::Display for CoordinateError { | |
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
write!(fmt,"The value {:?} is not a valid coordinate", self) | |
} | |
} | |
impl Error for CoordinateError { | |
fn description(&self) -> &str { | |
"The value is not a valid coordinate" | |
} | |
} | |
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] | |
pub struct Coordinate { | |
index: u64, | |
depth: u8, | |
} | |
impl Coordinate { | |
fn to_i64(&self) -> Result<i64,CoordinateError> { | |
if self.depth as usize > COORDINATE_MAX_DEPTH || self.index > COORDINATE_INDEX_MASK { | |
Err(CoordinateError::To(*self)) | |
} else { | |
Ok((((self.depth as u64)<<COORDINATE_MAX_DEPTH) | (self.index & COORDINATE_INDEX_MASK)) as i64) | |
} | |
} | |
fn from_i64(i: i64) -> Result<Self,CoordinateError> { | |
let depth=((i as u64)>>COORDINATE_MAX_DEPTH) as u8; | |
let index=(i as u64)&COORDINATE_INDEX_MASK; | |
if depth>58 { | |
Err(CoordinateError::From(i)) | |
} else { | |
Ok(Coordinate{depth:depth,index:index}) | |
} | |
} | |
} | |
impl<DB> FromSqlRow<BigInt, DB> for Coordinate where DB: Backend + HasSqlType<BigInt>, i64: FromSql<BigInt, DB> { | |
fn build_from_row<T: Row<DB>>(row: &mut T) -> Result<Self, Box<Error + Send + Sync>> { | |
<i64 as FromSqlRow<BigInt, DB>>::build_from_row(row).and_then(|i|Coordinate::from_i64(i).map_err(|e|Box::new(e) as _)) | |
} | |
} | |
impl<DB> Queryable<BigInt, DB> for Coordinate where DB: Backend + HasSqlType<BigInt>, Coordinate: FromSqlRow<BigInt, DB> { | |
type Row = Self; | |
fn build(row: Self::Row) -> Self { | |
row | |
} | |
} | |
impl<DB: Backend<RawValue=[u8]>> FromSql<BigInt, DB> for Coordinate { | |
fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error + Send + Sync>> { | |
<i64 as FromSql<_,DB>>::from_sql(bytes).and_then(|i|Coordinate::from_i64(i).map_err(|e|Box::new(e) as _)) | |
} | |
} | |
impl<DB: Backend> ToSql<BigInt, DB> for Coordinate { | |
fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error + Send + Sync>> { | |
let i=try!(self.to_i64().map_err(Box::new)); | |
<i64 as ToSql<BigInt,DB>>::to_sql(&i, out) | |
} | |
} | |
impl AsExpression<BigInt> for Coordinate { | |
type Expression = <i64 as AsExpression<BigInt>>::Expression; | |
fn as_expression(self) -> Self::Expression { | |
<i64 as AsExpression<BigInt>>::as_expression(self.to_i64().expect("The value is not a valid coordinate")) | |
} | |
} | |
impl<'a> AsExpression<BigInt> for &'a Coordinate { | |
type Expression = <i64 as AsExpression<BigInt>>::Expression; | |
fn as_expression(self) -> Self::Expression { | |
<i64 as AsExpression<BigInt>>::as_expression(self.to_i64().expect("The value is not a valid coordinate")) | |
} | |
} | |
impl AsExpression<Nullable<BigInt>> for Coordinate { | |
type Expression = <i64 as AsExpression<Nullable<BigInt>>>::Expression; | |
fn as_expression(self) -> Self::Expression { | |
<i64 as AsExpression<Nullable<BigInt>>>::as_expression(self.to_i64().expect("The value is not a valid coordinate")) | |
} | |
} | |
impl<'a> AsExpression<Nullable<BigInt>> for &'a Coordinate { | |
type Expression = <i64 as AsExpression<Nullable<BigInt>>>::Expression; | |
fn as_expression(self) -> Self::Expression { | |
<i64 as AsExpression<Nullable<BigInt>>>::as_expression(self.to_i64().expect("The value is not a valid coordinate")) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment