use crate::common::CompactArc;
use crate::core::{Error, Result, Row, Value};
use crate::storage::traits::QueryResult;
use super::database::FromValue;
pub trait FromRow: Sized {
fn from_row(row: &ResultRow) -> Result<Self>;
}
#[derive(Debug, Clone)]
pub struct ResultRow {
row: Row,
columns: CompactArc<Vec<String>>,
}
impl ResultRow {
pub(crate) fn new(row: Row, columns: CompactArc<Vec<String>>) -> Self {
Self { row, columns }
}
pub fn get<T: FromValue>(&self, index: usize) -> Result<T> {
let value = self
.row
.get(index)
.ok_or(Error::ColumnIndexOutOfBounds { index })?;
T::from_value(value)
}
pub fn get_by_name<T: FromValue>(&self, name: &str) -> Result<T> {
let index = self.column_index(name)?;
self.get(index)
}
pub fn get_value(&self, index: usize) -> Option<&Value> {
self.row.get(index)
}
pub fn into_inner(self) -> Row {
self.row
}
pub fn as_row(&self) -> &Row {
&self.row
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn len(&self) -> usize {
self.row.len()
}
pub fn is_empty(&self) -> bool {
self.row.len() == 0
}
pub fn is_null(&self, index: usize) -> bool {
self.row.get(index).map(|v| v.is_null()).unwrap_or(true)
}
fn column_index(&self, name: &str) -> Result<usize> {
let name_lower = name.to_lowercase();
self.columns
.iter()
.position(|c| c.to_lowercase() == name_lower)
.ok_or_else(|| Error::ColumnNotFound(name.to_string()))
}
}
pub struct Rows {
result: Box<dyn QueryResult>,
columns: CompactArc<Vec<String>>,
closed: bool,
pending_error: Option<crate::core::Error>,
}
impl Rows {
pub(crate) fn new(result: Box<dyn QueryResult>) -> Self {
let columns = result
.columns_arc()
.unwrap_or_else(|| CompactArc::new(result.columns().to_vec()));
Self {
result,
columns,
closed: false,
pending_error: None,
}
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn column_count(&self) -> usize {
self.columns.len()
}
pub fn rows_affected(&self) -> i64 {
self.result.rows_affected()
}
#[inline]
pub fn advance(&mut self) -> bool {
if self.closed {
return false;
}
if self.result.next() {
return true;
}
if let Some(err) = self.result.last_error() {
self.pending_error = Some(err);
}
self.close();
false
}
#[inline]
pub fn error(&mut self) -> Option<crate::core::Error> {
self.pending_error.take()
}
#[inline]
pub fn current_row(&self) -> &Row {
self.result.row()
}
pub fn collect_vec(self) -> Result<Vec<ResultRow>> {
self.collect()
}
pub fn close(&mut self) {
if !self.closed {
let _ = self.result.close();
self.closed = true;
}
}
}
impl Iterator for Rows {
type Item = Result<ResultRow>;
fn next(&mut self) -> Option<Self::Item> {
if self.closed {
return None;
}
if self.result.next() {
let row = self.result.take_row();
Some(Ok(ResultRow::new(row, CompactArc::clone(&self.columns))))
} else if let Some(err) = self.result.last_error() {
self.close();
Some(Err(err))
} else {
None
}
}
}
impl Drop for Rows {
fn drop(&mut self) {
self.close();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::traits::MemoryResult;
fn create_test_rows() -> Rows {
let columns = vec!["id".to_string(), "name".to_string(), "value".to_string()];
let rows = vec![
Row::from_values(vec![
Value::Integer(1),
Value::text("Alice"),
Value::Float(10.5),
]),
Row::from_values(vec![
Value::Integer(2),
Value::text("Bob"),
Value::Float(20.0),
]),
];
let result = MemoryResult::with_rows(columns, rows);
Rows::new(Box::new(result))
}
#[test]
fn test_iterator_for_loop() {
let rows = create_test_rows();
let mut count = 0;
for row in rows {
let row = row.unwrap();
assert!(row.get::<i64>(0).is_ok());
count += 1;
}
assert_eq!(count, 2);
}
#[test]
fn test_iterator_collect() {
let rows = create_test_rows();
let collected: Vec<ResultRow> = rows.collect::<std::result::Result<Vec<_>, _>>().unwrap();
assert_eq!(collected.len(), 2);
assert_eq!(collected[0].get::<i64>(0).unwrap(), 1);
assert_eq!(collected[1].get::<i64>(0).unwrap(), 2);
}
#[test]
fn test_iterator_map() {
let rows = create_test_rows();
let names: Vec<String> = rows
.map(|r| r.and_then(|row| row.get(1)))
.collect::<std::result::Result<Vec<_>, _>>()
.unwrap();
assert_eq!(names, vec!["Alice", "Bob"]);
}
#[test]
fn test_iterator_filter() {
let rows = create_test_rows();
let filtered: Vec<ResultRow> = rows
.filter_map(|r| {
let row = r.ok()?;
let id: i64 = row.get(0).ok()?;
if id > 1 {
Some(row)
} else {
None
}
})
.collect();
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].get::<String>(1).unwrap(), "Bob");
}
#[test]
fn test_result_row_get() {
let rows = create_test_rows();
let row = rows.into_iter().next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1);
assert_eq!(row.get::<String>(1).unwrap(), "Alice");
assert_eq!(row.get::<f64>(2).unwrap(), 10.5);
}
#[test]
fn test_result_row_get_by_name() {
let rows = create_test_rows();
let row = rows.into_iter().next().unwrap().unwrap();
assert_eq!(row.get_by_name::<i64>("id").unwrap(), 1);
assert_eq!(row.get_by_name::<String>("name").unwrap(), "Alice");
assert_eq!(row.get_by_name::<f64>("value").unwrap(), 10.5);
assert_eq!(row.get_by_name::<i64>("ID").unwrap(), 1);
assert_eq!(row.get_by_name::<String>("NAME").unwrap(), "Alice");
}
#[test]
fn test_result_row_columns() {
let rows = create_test_rows();
let row = rows.into_iter().next().unwrap().unwrap();
assert_eq!(row.columns(), &["id", "name", "value"]);
assert_eq!(row.len(), 3);
assert!(!row.is_empty());
}
#[test]
fn test_rows_columns() {
let rows = create_test_rows();
assert_eq!(rows.columns(), &["id", "name", "value"]);
assert_eq!(rows.column_count(), 3);
}
#[test]
fn test_collect_vec() {
let rows = create_test_rows();
let collected = rows.collect_vec().unwrap();
assert_eq!(collected.len(), 2);
}
#[test]
fn test_out_of_bounds() {
let rows = create_test_rows();
let row = rows.into_iter().next().unwrap().unwrap();
assert!(row.get::<i64>(10).is_err());
}
#[test]
fn test_column_not_found() {
let rows = create_test_rows();
let row = rows.into_iter().next().unwrap().unwrap();
assert!(row.get_by_name::<String>("nonexistent").is_err());
}
#[test]
fn test_advance_and_current_row() {
let mut rows = create_test_rows();
assert!(rows.advance());
let row = rows.current_row();
assert_eq!(row.get(0), Some(&Value::Integer(1)));
assert_eq!(row.get(1), Some(&Value::text("Alice")));
assert!(rows.advance());
let row = rows.current_row();
assert_eq!(row.get(0), Some(&Value::Integer(2)));
assert_eq!(row.get(1), Some(&Value::text("Bob")));
assert!(!rows.advance());
}
#[test]
fn test_advance_on_closed_rows() {
let mut rows = create_test_rows();
rows.close();
assert!(!rows.advance());
}
#[test]
fn test_advance_full_scan() {
let mut rows = create_test_rows();
let mut count = 0;
while rows.advance() {
let _row = rows.current_row();
count += 1;
}
assert_eq!(count, 2);
}
}