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");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_raw_row_default() {
let row = RawRow::default();
assert!(row.is_empty());
assert_eq!(row.len(), 0);
}
#[test]
fn test_raw_row_basic_operations() {
let mut row = RawRow::default();
assert!(row.is_empty());
assert_eq!(row.len(), 0);
row.set(&"test_col", 42i32);
assert!(!row.is_empty());
assert_eq!(row.len(), 1);
row.try_set(&"test_col2", "hello").unwrap();
assert_eq!(row.len(), 2);
}
#[test]
fn test_raw_row_set_typed() {
let mut row = RawRow::default();
row.set_typed(&"int_col", Some(Type::Int32), 123i32);
assert_eq!(row.len(), 1);
row.try_set_typed(&"str_col", Some(Type::String), "test").unwrap();
assert_eq!(row.len(), 2);
row.try_set_typed(&"int_col", Some(Type::Int32), 456i32).unwrap();
assert_eq!(row.len(), 2); }
#[test]
fn test_raw_row_get_by_index() {
let mut row = RawRow::default();
row.set(&"col1", 42i32);
row.set(&"col2", "hello");
let val: i32 = row.try_get(0usize).unwrap();
assert_eq!(val, 42);
}
#[test]
fn test_raw_row_get_by_name() {
let mut row = RawRow::default();
row.set(&"test_column", 123i64);
let val: i64 = row.try_get("test_column").unwrap();
assert_eq!(val, 123);
}
#[test]
fn test_raw_row_get_panics() {
let mut row = RawRow::default();
row.set(&"test", 42i32);
let _val: i32 = row.get(0usize);
drop(
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _val2: i32 = row.get(0usize);
}))
.unwrap_err(),
);
}
#[test]
fn test_raw_row_errors() {
let mut row = RawRow::default();
row.set(&"test", 42i32);
let result: Result<i32> = row.try_get(10usize);
assert!(matches!(result, Err(Error::OutOfBounds)));
let _val: i32 = row.try_get(0usize).unwrap();
let result2: Result<i32> = row.try_get(0usize);
assert!(matches!(result2, Err(Error::DoubleFetch)));
}
#[test]
fn test_raw_row_into_values() {
let mut row = RawRow::default();
row.set(&"col1", 42i32);
row.set(&"col2", "test");
let values = row.into_values();
assert_eq!(values.len(), 2);
assert_eq!(values[0].0, Type::Int32);
assert_eq!(values[1].0, Type::String);
}
#[test]
fn test_row_index_usize() {
let columns = ["col1", "col2", "col3"];
assert_eq!(1usize.get(columns.iter().copied()), Some(1));
assert_eq!(5usize.get(columns.iter().copied()), None);
}
#[test]
fn test_row_index_str() {
let columns = ["col1", "col2", "col3"];
assert_eq!(RowIndex::get("col2", columns.iter().copied()), Some(1));
assert_eq!(RowIndex::get("nonexistent", columns.iter().copied()), None);
}
#[test]
fn test_row_index_ref() {
let columns = ["col1", "col2", "col3"];
let name = "col2";
assert_eq!((&name).get(columns.iter().copied()), Some(1));
assert_eq!((1usize).get(columns.iter().copied()), Some(1));
}
#[test]
fn test_raw_row_deserialize() {
let map = vec![
("col1", &Type::Int32, Value::Int32(42)),
("col2", &Type::String, Value::String("test".to_string().into_bytes())),
];
let row = RawRow::deserialize_row(map).unwrap();
assert_eq!(row.len(), 2);
assert!(!row.is_empty());
}
#[test]
fn test_raw_row_serialize() {
let mut row = RawRow::default();
row.set(&"col1", 42i32);
row.set(&"col2", "test");
let serialized = row.serialize_row(&[]).unwrap();
assert_eq!(serialized.len(), 2);
assert_eq!(serialized[0].0, "col1");
assert_eq!(serialized[1].0, "col2");
}
#[test]
fn test_raw_row_serialize_panic() {
let raw_row = RawRow(vec![None]);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
raw_row.serialize_row(&[]).unwrap()
}));
assert!(result.is_err());
}
#[test]
fn test_raw_row_static_methods() {
assert_eq!(RawRow::COLUMN_COUNT, None);
assert_eq!(RawRow::column_names(), None);
assert_eq!(RawRow::to_schema(), None);
}
}