Skip to content

Instantly share code, notes, and snippets.

@jethrogb
Last active August 17, 2021 10:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jethrogb/bd06d490cf1ec01748b5327877364670 to your computer and use it in GitHub Desktop.
Save jethrogb/bd06d490cf1ec01748b5327877364670 to your computer and use it in GitHub Desktop.
Custom Diesel wrapper type example
/// 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