use core::{fmt, marker::PhantomData, ops::Range};
use std::sync::Arc;
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::DataRowBody;
use postgres_types::FromSql;
use xitca_io::bytes::{Bytes, BytesStr};
use crate::{
column::Column,
error::{Error, InvalidColumnIndex, WrongType},
from_sql::FromSqlExt,
types::Type,
};
use super::{marker, traits::RowIndexAndType};
pub type Row<'r> = GenericRow<&'r [Column], &'r mut Vec<Range<usize>>, marker::Typed>;
pub type RowSimple<'r> = GenericRow<&'r [Column], &'r mut Vec<Range<usize>>, marker::NoTyped>;
pub type RowOwned = GenericRow<Arc<[Column]>, Vec<Range<usize>>, marker::Typed>;
pub type RowSimpleOwned = GenericRow<Arc<[Column]>, Vec<Range<usize>>, marker::NoTyped>;
pub struct GenericRow<C, R, M> {
columns: C,
body: DataRowBody,
ranges: R,
_marker: PhantomData<M>,
}
impl<C, R, M> fmt::Debug for GenericRow<C, R, M>
where
C: AsRef<[Column]>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Row").field("columns", &self.columns.as_ref()).finish()
}
}
impl<C, R, M> GenericRow<C, R, M>
where
C: AsRef<[Column]>,
R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
{
pub(crate) fn try_new(columns: C, body: DataRowBody, mut ranges: R) -> Result<Self, Error> {
let mut iter = body.ranges();
let ranges_mut = ranges.as_mut();
ranges_mut.clear();
ranges_mut.reserve(iter.size_hint().0);
while let Some(range) = iter.next()? {
ranges_mut.push(range.unwrap_or(Range { start: 1, end: 0 }));
}
Ok(Self {
columns,
body,
ranges,
_marker: PhantomData,
})
}
#[inline]
pub fn columns(&self) -> &[Column] {
self.columns.as_ref()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn len(&self) -> usize {
self.columns().len()
}
fn col_buffer(&self, idx: usize) -> (&Range<usize>, &Bytes) {
(&self.ranges.as_ref()[idx], self.body.buffer_bytes())
}
fn get_idx_ty<T>(
&self,
idx: impl RowIndexAndType + fmt::Display,
ty_check: impl FnOnce(&Type) -> bool,
) -> Result<(usize, &Type), Error> {
let (idx, ty) = idx
._from_columns(self.columns.as_ref())
.ok_or_else(|| InvalidColumnIndex(idx.to_string()))?;
if !ty_check(ty) {
return Err(Error::from(WrongType::new::<T>(ty.clone())));
}
Ok((idx, ty))
}
}
impl<C, R> GenericRow<C, R, marker::Typed>
where
C: AsRef<[Column]>,
R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
{
pub fn get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
where
T: FromSqlExt<'s>,
{
self.try_get_zc(idx)
.unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
}
pub fn try_get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
where
T: FromSqlExt<'s>,
{
let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts)?;
FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
}
pub fn get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
where
T: FromSql<'s>,
{
self.try_get(idx)
.unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
}
pub fn try_get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
where
T: FromSql<'s>,
{
let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts)?;
FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
}
}
impl<C, R> GenericRow<C, R, marker::NoTyped>
where
C: AsRef<[Column]>,
R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
{
pub fn get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<BytesStr> {
self.try_get_zc(idx)
.unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
}
pub fn try_get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<BytesStr>, Error> {
let (idx, ty) = self.get_idx_ty::<BytesStr>(idx, BytesStr::accepts)?;
FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
}
pub fn get(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<&str> {
self.try_get(idx)
.unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
}
pub fn try_get(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<&str>, Error> {
let (idx, ty) = self.get_idx_ty::<&str>(idx, <&str as FromSql>::accepts)?;
FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
}
}
fn _try_get(row: Row) {
let _ = row.try_get::<u32>(0);
let _ = row.try_get_zc::<BytesStr>("test");
let _ = row.try_get_zc::<Bytes>(String::from("get_raw").as_str());
}
fn _try_get_simple(row: RowSimple) {
let _ = row.try_get_zc(0);
let _ = row.get_zc("test");
let _ = row.try_get(String::from("get_raw").as_str());
}