use core::ops::Range;
use xitca_io::bytes::Bytes;
use xitca_unsafe_collection::bytes::BytesStr;
use super::types::{FromSql, Type};
pub type FromSqlError = Box<dyn std::error::Error + Sync + Send>;
pub trait FromSqlExt<'a>: Sized {
fn from_sql_nullable_ext(ty: &Type, col: (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError>;
fn accepts(ty: &Type) -> bool;
}
macro_rules! default_impl {
($ty: ty) => {
impl<'a> FromSqlExt<'a> for $ty {
default_impl!();
}
};
() => {
#[inline]
fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
<Self as FromSql>::from_sql_nullable(ty, buf.get(range.clone()))
}
#[inline]
fn accepts(ty: &Type) -> bool {
<Self as FromSql>::accepts(ty)
}
};
}
impl<'a, T> FromSqlExt<'a> for Option<T>
where
T: FromSql<'a>,
{
default_impl!();
}
impl<'a, T> FromSqlExt<'a> for Vec<T>
where
T: FromSql<'a>,
{
default_impl!();
}
impl<'a, T> FromSqlExt<'a> for Box<[T]>
where
T: FromSql<'a>,
{
default_impl!();
}
default_impl!(&'a [u8]);
default_impl!(&'a str);
default_impl!(String);
default_impl!(Box<str>);
default_impl!(bool);
default_impl!(i8);
default_impl!(i16);
default_impl!(i32);
default_impl!(u32);
default_impl!(i64);
default_impl!(f32);
default_impl!(f64);
impl<'a> FromSqlExt<'a> for BytesStr {
fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
fn adjust_range(name: &str, range: &Range<usize>, buf: &Bytes) -> Result<(usize, usize), FromSqlError> {
if buf[range.start] == 1u8 {
Ok((range.start + 1, range.end))
} else {
Err(format!("only {name} version 1 supported").into())
}
}
if range.is_empty() {
return <&str as FromSql>::from_sql_null(ty)
.map(|_| unreachable!("<&str as FromSql>::from_sql_null should always yield Result::Err branch"));
}
let (start, end) = match ty.name() {
"ltree" => adjust_range("ltree", range, buf)?,
"lquery" => adjust_range("lquery", range, buf)?,
"ltxtquery" => adjust_range("ltxtquery", range, buf)?,
_ => (range.start, range.end),
};
BytesStr::try_from(buf.slice(start..end)).map_err(Into::into)
}
#[inline]
fn accepts(ty: &Type) -> bool {
<&str as FromSql>::accepts(ty)
}
}
impl<'a> FromSqlExt<'a> for Bytes {
fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
if range.is_empty() {
return <&[u8] as FromSql>::from_sql_null(ty)
.map(|_| unreachable!("<&[u8] as FromSql>::from_sql_null should always yield Result::Err branch"));
}
Ok(buf.slice(range.start..range.end))
}
#[inline]
fn accepts(ty: &Type) -> bool {
<&[u8] as FromSql>::accepts(ty)
}
}