use crate::error::DrizzleError;
use crate::row::FromDrizzleRow;
#[derive(Debug, Clone)]
pub enum SqliteCell {
Null,
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
}
impl SqliteCell {
#[inline]
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
}
pub trait SqliteValueRow {
fn cell_at(&self, offset: usize) -> Result<SqliteCell, DrizzleError>;
#[inline]
fn is_null_at(&self, offset: usize) -> Result<bool, DrizzleError> {
Ok(self.cell_at(offset)?.is_null())
}
}
#[inline]
fn i64_to_f64(i: i64) -> f64 {
let high = i32::try_from(i >> 32).expect("sign-extended high word fits in i32");
let low = u32::try_from(i & 0xFFFF_FFFF).expect("masked low word fits in u32");
f64::from(high) * 4_294_967_296.0_f64 + f64::from(low)
}
macro_rules! sqlite_value_int_impl {
($($ty:ty),*) => { $(
impl<R: SqliteValueRow> FromDrizzleRow<R> for $ty {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Integer(i) => i.try_into().map_err(
|e: core::num::TryFromIntError| {
DrizzleError::ConversionError(e.to_string().into())
},
),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for integer".into(),
)),
_ => Err(DrizzleError::ConversionError(
"expected integer value".into(),
)),
}
}
}
)* }
}
sqlite_value_int_impl!(i8, i16, i32, isize, u8, u16, u32, u64, usize);
impl<R: SqliteValueRow> FromDrizzleRow<R> for i64 {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Integer(i) => Ok(i),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for integer".into(),
)),
_ => Err(DrizzleError::ConversionError(
"expected integer value".into(),
)),
}
}
}
impl<R: SqliteValueRow> FromDrizzleRow<R> for f64 {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Real(r) => Ok(r),
SqliteCell::Integer(i) => Ok(i64_to_f64(i)),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for float".into(),
)),
_ => Err(DrizzleError::ConversionError("expected real value".into())),
}
}
}
impl<R: SqliteValueRow> FromDrizzleRow<R> for f32 {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let v = f64::from_row_at(row, offset)?;
let f: Self = format!("{v}")
.parse()
.map_err(|e: core::num::ParseFloatError| {
DrizzleError::ConversionError(e.to_string().into())
})?;
if v.is_finite() && !f.is_finite() {
return Err(DrizzleError::ConversionError(
format!("f64 value {v} overflows f32").into(),
));
}
Ok(f)
}
}
impl<R: SqliteValueRow> FromDrizzleRow<R> for bool {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Integer(i) => Ok(i != 0),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for bool".into(),
)),
_ => Err(DrizzleError::ConversionError(
"expected integer for bool".into(),
)),
}
}
}
impl<R: SqliteValueRow> FromDrizzleRow<R> for String {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Text(s) => Ok(s),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for string".into(),
)),
_ => Err(DrizzleError::ConversionError("expected text value".into())),
}
}
}
impl<R: SqliteValueRow> FromDrizzleRow<R> for Vec<u8> {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Blob(b) => Ok(b),
SqliteCell::Null => Err(DrizzleError::ConversionError(
"unexpected NULL for blob".into(),
)),
_ => Err(DrizzleError::ConversionError("expected blob value".into())),
}
}
}
impl<R: SqliteValueRow, T> FromDrizzleRow<R> for Option<T>
where
T: FromDrizzleRow<R>,
{
const COLUMN_COUNT: usize = T::COLUMN_COUNT;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
if row.is_null_at(offset)? {
Ok(None)
} else {
T::from_row_at(row, offset).map(Some)
}
}
}
#[cfg(feature = "uuid")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for uuid::Uuid {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
match row.cell_at(offset)? {
SqliteCell::Text(s) => Self::parse_str(&s).map_err(Into::into),
SqliteCell::Blob(b) => Self::from_slice(&b)
.map_err(|e| DrizzleError::ConversionError(e.to_string().into())),
_ => Err(DrizzleError::ConversionError(
"expected TEXT or BLOB for UUID".into(),
)),
}
}
}
#[cfg(feature = "chrono")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for chrono::NaiveDate {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let s = String::from_row_at(row, offset)?;
s.parse()
.map_err(|e: chrono::ParseError| DrizzleError::ConversionError(e.to_string().into()))
}
}
#[cfg(feature = "chrono")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for chrono::NaiveTime {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let s = String::from_row_at(row, offset)?;
s.parse()
.map_err(|e: chrono::ParseError| DrizzleError::ConversionError(e.to_string().into()))
}
}
#[cfg(feature = "chrono")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for chrono::NaiveDateTime {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let s = String::from_row_at(row, offset)?;
s.parse()
.map_err(|e: chrono::ParseError| DrizzleError::ConversionError(e.to_string().into()))
}
}
#[cfg(feature = "chrono")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for chrono::DateTime<chrono::Utc> {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let s = String::from_row_at(row, offset)?;
let ndt: chrono::NaiveDateTime = s
.parse()
.map_err(|e: chrono::ParseError| DrizzleError::ConversionError(e.to_string().into()))?;
Ok(Self::from_naive_utc_and_offset(ndt, chrono::Utc))
}
}
#[cfg(feature = "serde")]
impl<R: SqliteValueRow> FromDrizzleRow<R> for serde_json::Value {
const COLUMN_COUNT: usize = 1;
fn from_row_at(row: &R, offset: usize) -> Result<Self, DrizzleError> {
let s = String::from_row_at(row, offset)?;
serde_json::from_str(&s).map_err(Into::into)
}
}