use std::borrow::Cow;
use crate::{Error, FromSql, Result, Row, ToSql, Type, Value};
#[derive(Debug, Default, Clone)]
pub struct RawRow(Vec<Option<(String, Type, Value)>>);
impl Row for RawRow {
const COLUMN_COUNT: Option<usize> = None;
fn column_names() -> Option<Vec<Cow<'static, str>>> { None }
fn to_schema() -> Option<Vec<(String, Type, Option<Value>)>> { None }
fn deserialize_row(map: Vec<(&str, &Type, Value)>) -> Result<Self> {
Ok(Self(
map.into_iter()
.map(|(name, type_, value)| Some((name.to_string(), type_.clone(), value)))
.collect(),
))
}
fn serialize_row(
self,
_type_hints: &[(String, Type)],
) -> Result<Vec<(Cow<'static, str>, Value)>> {
Ok(self
.0
.into_iter()
.map(|x| x.expect("cannot serialize a Row which has been retrieved from"))
.map(|(name, _, value)| (Cow::Owned(name), value))
.collect())
}
}
pub trait RowIndex {
fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize>;
}
impl RowIndex for usize {
fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
let count = columns.into_iter().count();
if count >= *self { Some(*self) } else { None }
}
}
impl RowIndex for str {
fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
columns.into_iter().position(|x| x == self)
}
}
impl<T: RowIndex + ?Sized> RowIndex for &T {
fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
(*self).get(columns)
}
}
impl RawRow {
pub fn is_empty(&self) -> bool { self.0.is_empty() }
pub fn len(&self) -> usize { self.0.len() }
pub fn into_values(self) -> Vec<(Type, Value)> {
self.0.into_iter().map(|x| x.map(|(_, t, v)| (t, v)).unwrap()).collect()
}
pub fn try_get<I: RowIndex, T: FromSql>(&mut self, index: I) -> Result<T> {
let index = index
.get(self.0.iter().map(|x| x.as_ref().map_or("", |x| &*x.0)))
.ok_or(Error::OutOfBounds)?;
let (_, type_, value) = self.0.get_mut(index).unwrap().take().ok_or(Error::DoubleFetch)?;
T::from_sql(&type_, value)
}
pub fn get<I: RowIndex, T: FromSql>(&mut self, index: I) -> T {
self.try_get(index).expect("failed to convert column")
}
pub fn try_set_typed(
&mut self,
name: &impl ToString,
type_: Option<Type>,
value: impl ToSql,
) -> Result<()> {
let name = name.to_string();
let value = value.to_sql(type_.as_ref())?;
let type_ = type_.unwrap_or_else(|| value.guess_type());
let current_position =
self.0.iter().map(|x| x.as_ref().map_or("", |x| &*x.0)).position(|x| x == &*name);
if let Some(current_position) = current_position {
self.0[current_position].as_mut().unwrap().1 = type_;
self.0[current_position].as_mut().unwrap().2 = value;
} else {
self.0.push(Some((name, type_, value)));
}
Ok(())
}
pub fn try_set(&mut self, name: &impl ToString, value: impl ToSql) -> Result<()> {
self.try_set_typed(name, None, value)
}
pub fn set(&mut self, name: &impl ToString, value: impl ToSql) {
self.try_set(name, value).expect("failed to convert column");
}
pub fn set_typed(&mut self, name: &impl ToString, type_: Option<Type>, value: impl ToSql) {
self.try_set_typed(name, type_, value).expect("failed to convert column");
}
}