use super::Modelable;
use sqlite::Bindable;
macro_rules! integral {
($i:ty) => {
impl Modelable for $i {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
(*self as i64).bind(stmt, col)
}
fn build_from(
stmt: &sqlite::Statement,
col_offset: usize,
) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
stmt.read::<i64>(col_offset).map(|x| (x as Self, 1))
}
fn column_type() -> &'static str
where
Self: Sized,
{
"integer"
}
}
};
}
integral!(i8);
integral!(u8);
integral!(i16);
integral!(u16);
integral!(i32);
integral!(u32);
integral!(i64);
integral!(u64);
integral!(usize);
impl Modelable for f64 {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
self.bind(stmt, col)
}
fn build_from(stmt: &sqlite::Statement, col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
stmt.read(col_offset).map(|x| (x, 1))
}
fn column_type() -> &'static str
where
Self: Sized,
{
"numeric"
}
}
impl Modelable for bool {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
let val = if self == &true { 1i64 } else { 0i64 };
val.bind(stmt, col)
}
fn build_from(stmt: &sqlite::Statement, col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
stmt.read(col_offset).map(|x: i64| (x != 0, 1))
}
fn column_type() -> &'static str
where
Self: Sized,
{
"integer"
}
}
impl<'a> Modelable for &'a str {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
self.bind(stmt, col)
}
fn build_from(_stmt: &sqlite::Statement, _col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
unreachable!("sqlite only gives Strings back, not &strs!");
}
fn column_type() -> &'static str
where
Self: Sized,
{
"text"
}
}
impl Modelable for str {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
self.bind(stmt, col)
}
}
impl Modelable for std::string::String {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
self.as_str().bind(stmt, col)
}
fn build_from(stmt: &sqlite::Statement, col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
stmt.read(col_offset).map(|x| (x, 1))
}
fn column_type() -> &'static str
where
Self: Sized,
{
"text"
}
}
impl<'a> Modelable for &'a [u8] {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
self.bind(stmt, col)
}
fn build_from(_stmt: &sqlite::Statement, _col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
unreachable!("sqlite only gives Vec<u8> back, not &[u8]!");
}
fn column_type() -> &'static str
where
Self: Sized,
{
"blob"
}
}
impl<'a, T: Modelable> Modelable for &'a T {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
<T as Modelable>::bind_to(self, stmt, col)
}
fn build_from(_stmt: &sqlite::Statement, _col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
unreachable!();
}
fn column_type() -> &'static str
where
Self: Sized,
{
unreachable!();
}
}
impl<T: Modelable> Modelable for Option<T> {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
match self.as_ref() {
Some(val) => val.bind_to(stmt, col),
None => stmt.bind(col, &sqlite::Value::Null),
}
}
fn build_from(stmt: &sqlite::Statement, col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
let val = stmt.read::<sqlite::Value>(col_offset)?;
if val.kind() == sqlite::Type::Null {
Ok((None, 1))
} else {
let (val, size) = T::build_from(stmt, col_offset)?;
Ok((Some(val), size))
}
}
fn column_type() -> &'static str
where
Self: Sized,
{
T::column_type()
}
}
impl<T: Modelable + serde::Serialize + serde::de::DeserializeOwned + 'static> Modelable for Vec<T> {
fn bind_to(&self, stmt: &mut sqlite::Statement, col: usize) -> sqlite::Result<()> {
if std::mem::size_of::<T>() == 1
&& std::any::TypeId::of::<T>() == std::any::TypeId::of::<u8>()
{
let byte_slice =
unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
return byte_slice.bind_to(stmt, col);
}
serde_json::to_string(self).unwrap().bind_to(stmt, col)
}
fn build_from(stmt: &sqlite::Statement, col_offset: usize) -> sqlite::Result<(Self, usize)>
where
Self: Sized,
{
if std::mem::size_of::<T>() == 1
&& std::any::TypeId::of::<T>() == std::any::TypeId::of::<u8>()
{
let blob: Vec<u8> = stmt.read(col_offset)?;
Ok((unsafe { std::mem::transmute::<Vec<u8>, Vec<T>>(blob) }, 1))
} else {
let s = String::build_from(stmt, col_offset)?;
Ok((
serde_json::from_str::<Vec<T>>(s.0.as_str()).map_err(|e| sqlite::Error {
code: None,
message: Some(e.to_string()),
})?,
1,
))
}
}
fn column_type() -> &'static str
where
Self: Sized,
{
"blob"
}
}