use rusqlite::{
Row,
Result as SqlResult,
Error
};
use rusqlite::types::{
ValueRef,
FromSql,
Type
};
pub trait Fetch: Sized {
fn fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Self>;
fn try_fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Option<Self>>;
fn from_row(row: &Row) -> SqlResult<Self> {
let mut fetcher = Fetcher::make(row);
Self::fetch(&mut fetcher)
}
}
pub trait FromSql2 {}
pub struct Fetcher<'row> {
index: usize,
all_nulls: bool,
row: &'row Row<'row>
}
impl<'row> Fetcher<'row> {
pub(crate) fn make(row: &'row Row<'row>) -> Self {
Self {index: 0, all_nulls: true, row}
}
pub(crate) fn reset_all_nulls(&mut self) {
self.all_nulls = true;
}
#[inline]
pub fn fetch_column<T: FromSql>(&mut self) -> SqlResult<T> {
let thing = self.row.get(self.index)?;
self.index += 1; Ok(thing)
}
#[inline]
pub fn try_fetch_column<T: FromSql>(&mut self) -> SqlResult<Option<T>> {
let thing = if self.row.get_ref(self.index)? != ValueRef::Null {
self.all_nulls = false;
Some(self.row.get(self.index)?)
}
else {None};
self.index += 1; Ok(thing)
}
#[inline]
#[must_use = "advances the column index"]
pub fn borrow_column(&'_ mut self) -> SqlResult<ValueRef<'_>> {
let value_ref = self.row.get_ref(self.index)?;
self.index += 1; Ok(value_ref)
}
pub fn fetch<T: Fetch>(&mut self) -> SqlResult<T> {
T::fetch(self)
}
pub fn try_fetch<T: Fetch>(&mut self) -> SqlResult<Option<T>> {
T::try_fetch(self)
}
pub fn fetch_none<T: Fetch>(&mut self) -> SqlResult<()> {
self.reset_all_nulls();
let _ = T::try_fetch(self)?;
if self.all_nulls {Ok(())}
else {Err(Error::FromSqlConversionFailure(
self.index, Type::Null,
"encountered non-NULL column expecting to fetch None".into()
))}
}
}
impl<T: FromSql + FromSql2> Fetch for T {
fn fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Self> {
fetcher.fetch_column()
}
fn try_fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Option<Self>> {
fetcher.try_fetch_column()
}
}
impl<T: Fetch> Fetch for Option<T> {
fn fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Self> {
T::try_fetch(fetcher)
}
fn try_fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Option<Self>> {
T::try_fetch(fetcher).map(Some)
}
}
#[liter_derive::impl_tuple(2..=16)]
impl Fetch for Each!(T) where Every!(T => T: Fetch): '_ {
fn fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Self> {
let fetched = each!{ fetcher.fetch()? };
Ok(fetched)
}
fn try_fetch(fetcher: &mut Fetcher<'_>) -> SqlResult<Option<Self>> {
fetcher.reset_all_nulls();
let fetched = each!{{
let idx = fetcher.index;
let all_nulls_before = fetcher.all_nulls;
let thing = fetcher.try_fetch()?;
fetcher.all_nulls &= all_nulls_before;
(idx, thing)
}};
let unwrapped = each!{
@fetched,
(idx, thing) => match thing {
Some(t) => t,
None if fetcher.all_nulls => return Ok(None),
None => return Err(Error::FromSqlConversionFailure(
idx, Type::Null,
"NULL column in optional value with non-NULL columns".into()
))
}
};
Ok(Some(unwrapped))
}
}
impl FromSql2 for bool {}
impl FromSql2 for i8 {}
impl FromSql2 for i16 {}
impl FromSql2 for i32 {}
impl FromSql2 for i64 {}
impl FromSql2 for isize {}
impl FromSql2 for u8 {}
impl FromSql2 for u16 {}
impl FromSql2 for u32 {}
impl FromSql2 for u64 {}
impl FromSql2 for usize {}
impl FromSql2 for f32 {}
impl FromSql2 for f64 {}
impl FromSql2 for String {}
impl FromSql2 for Box<str> {}
impl FromSql2 for std::rc::Rc<str> {}
impl FromSql2 for std::sync::Arc<str> {}
impl FromSql2 for Vec<u8> {}
impl<const N: usize> FromSql2 for [u8; N] {}
impl FromSql2 for rusqlite::types::Value {}