use std::fmt;
use std::ops::{Deref, DerefMut, Index, IndexMut};
use super::error::{Error, Result};
use super::schema::Schema;
use super::types::DataType;
use super::value::Value;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Row {
values: Vec<Value>,
}
impl Row {
pub fn new() -> Self {
Self { values: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
values: Vec::with_capacity(capacity),
}
}
pub fn from_values(values: Vec<Value>) -> Self {
Self { values }
}
pub fn null_row(schema: &Schema) -> Self {
let values = schema
.columns
.iter()
.map(|col| Value::null(col.data_type))
.collect();
Self { values }
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn get(&self, index: usize) -> Option<&Value> {
self.values.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
self.values.get_mut(index)
}
pub fn set(&mut self, index: usize, value: Value) -> Result<()> {
if index >= self.values.len() {
return Err(Error::Internal {
message: format!(
"row index {} out of bounds (len={})",
index,
self.values.len()
),
});
}
self.values[index] = value;
Ok(())
}
pub fn push(&mut self, value: Value) {
self.values.push(value);
}
pub fn pop(&mut self) -> Option<Value> {
self.values.pop()
}
pub fn truncate(&mut self, len: usize) {
self.values.truncate(len);
}
#[inline]
pub fn clear(&mut self) {
self.values.clear();
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.values.reserve(additional);
}
pub fn iter(&self) -> impl Iterator<Item = &Value> {
self.values.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Value> {
self.values.iter_mut()
}
pub fn into_values(self) -> Vec<Value> {
self.values
}
pub fn as_slice(&self) -> &[Value] {
&self.values
}
pub fn as_mut_slice(&mut self) -> &mut [Value] {
&mut self.values
}
pub fn select_columns(&self, indices: &[usize]) -> Result<Row> {
let mut values = Vec::with_capacity(indices.len());
for &idx in indices {
match self.values.get(idx) {
Some(v) => values.push(v.clone()),
None => {
return Err(Error::Internal {
message: format!(
"column index {} out of bounds (len={})",
idx,
self.values.len()
),
})
}
}
}
Ok(Row::from_values(values))
}
#[inline]
pub fn take_columns(mut self, indices: &[usize]) -> Row {
let mut values = Vec::with_capacity(indices.len());
for &idx in indices {
if idx < self.values.len() {
values.push(std::mem::take(&mut self.values[idx]));
} else {
values.push(Value::null_unknown());
}
}
Row::from_values(values)
}
pub fn validate(&self, schema: &Schema) -> Result<()> {
if self.values.len() != schema.columns.len() {
return Err(Error::table_columns_not_match(
schema.columns.len(),
self.values.len(),
));
}
for (i, (value, col)) in self.values.iter().zip(schema.columns.iter()).enumerate() {
if value.is_null() && !col.nullable && !col.primary_key {
return Err(Error::not_null_constraint(&col.name));
}
if !value.is_null() {
let value_type = value.data_type();
if value_type != col.data_type {
let compatible = matches!(
(value_type, col.data_type),
(DataType::Integer, DataType::Float) | (DataType::Float, DataType::Integer)
);
if !compatible {
return Err(Error::type_conversion(
format!("column {} at index {}: {:?}", col.name, i, value_type),
format!("{:?}", col.data_type),
));
}
}
}
}
Ok(())
}
pub fn clone_subset(&self, indices: &[usize]) -> Row {
let values = indices
.iter()
.filter_map(|&i| self.values.get(i).cloned())
.collect();
Row::from_values(values)
}
pub fn concat(&self, other: &Row) -> Row {
let mut values = self.values.clone();
values.extend(other.values.iter().cloned());
Row::from_values(values)
}
pub fn repeat(value: Value, count: usize) -> Row {
Row::from_values(vec![value; count])
}
}
impl Deref for Row {
type Target = Vec<Value>;
fn deref(&self) -> &Self::Target {
&self.values
}
}
impl DerefMut for Row {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.values
}
}
impl Index<usize> for Row {
type Output = Value;
fn index(&self, index: usize) -> &Self::Output {
&self.values[index]
}
}
impl IndexMut<usize> for Row {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.values[index]
}
}
impl FromIterator<Value> for Row {
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
Row::from_values(iter.into_iter().collect())
}
}
impl IntoIterator for Row {
type Item = Value;
type IntoIter = std::vec::IntoIter<Value>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
impl<'a> IntoIterator for &'a Row {
type Item = &'a Value;
type IntoIter = std::slice::Iter<'a, Value>;
fn into_iter(self) -> Self::IntoIter {
self.values.iter()
}
}
impl<'a> IntoIterator for &'a mut Row {
type Item = &'a mut Value;
type IntoIter = std::slice::IterMut<'a, Value>;
fn into_iter(self) -> Self::IntoIter {
self.values.iter_mut()
}
}
impl From<Vec<Value>> for Row {
fn from(values: Vec<Value>) -> Self {
Row::from_values(values)
}
}
impl fmt::Display for Row {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(")?;
for (i, value) in self.values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
write!(f, ")")
}
}
#[macro_export]
macro_rules! row {
() => {
$crate::core::Row::new()
};
($($value:expr),+ $(,)?) => {
$crate::core::Row::from_values(vec![$($crate::core::Value::from($value)),+])
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::schema::SchemaBuilder;
fn create_test_schema() -> Schema {
SchemaBuilder::new("test")
.add_primary_key("id", DataType::Integer)
.add("name", DataType::Text)
.add_nullable("email", DataType::Text)
.build()
}
#[test]
fn test_row_creation() {
let row = Row::new();
assert!(row.is_empty());
assert_eq!(row.len(), 0);
let row = Row::with_capacity(10);
assert!(row.is_empty());
}
#[test]
fn test_row_from_values() {
let values = vec![
Value::integer(1),
Value::text("hello"),
Value::null(DataType::Text),
];
let row = Row::from_values(values);
assert_eq!(row.len(), 3);
}
#[test]
fn test_row_push_pop() {
let mut row = Row::new();
row.push(Value::integer(1));
row.push(Value::text("hello"));
assert_eq!(row.len(), 2);
let popped = row.pop();
assert_eq!(popped, Some(Value::text("hello")));
assert_eq!(row.len(), 1);
}
#[test]
fn test_row_get_set() {
let mut row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
assert_eq!(row.get(0), Some(&Value::integer(1)));
assert_eq!(row.get(1), Some(&Value::text("hello")));
assert_eq!(row.get(2), None);
row.set(1, Value::text("world")).unwrap();
assert_eq!(row.get(1), Some(&Value::text("world")));
assert!(row.set(10, Value::integer(0)).is_err());
}
#[test]
fn test_row_index() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
assert_eq!(row[0], Value::integer(1));
assert_eq!(row[1], Value::text("hello"));
}
#[test]
fn test_row_iteration() {
let row = Row::from_values(vec![
Value::integer(1),
Value::integer(2),
Value::integer(3),
]);
let sum: i64 = row.iter().filter_map(|v| v.as_int64()).sum();
assert_eq!(sum, 6);
}
#[test]
fn test_row_select_columns() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::float(3.5),
Value::boolean(true),
]);
let selected = row.select_columns(&[0, 2]).unwrap();
assert_eq!(selected.len(), 2);
assert_eq!(selected[0], Value::integer(1));
assert_eq!(selected[1], Value::float(3.5));
assert!(row.select_columns(&[0, 10]).is_err());
}
#[test]
fn test_row_validate() {
let schema = create_test_schema();
let row = Row::from_values(vec![
Value::integer(1),
Value::text("Alice"),
Value::null(DataType::Text),
]);
assert!(row.validate(&schema).is_ok());
let row = Row::from_values(vec![Value::integer(1)]);
assert!(row.validate(&schema).is_err());
let row = Row::from_values(vec![
Value::integer(1),
Value::null(DataType::Text), Value::null(DataType::Text),
]);
let err = row.validate(&schema).unwrap_err();
assert!(matches!(err, Error::NotNullConstraint { .. }));
}
#[test]
fn test_row_null_row() {
let schema = create_test_schema();
let row = Row::null_row(&schema);
assert_eq!(row.len(), 3);
assert!(row[0].is_null());
assert!(row[1].is_null());
assert!(row[2].is_null());
}
#[test]
fn test_row_concat() {
let row1 = Row::from_values(vec![Value::integer(1), Value::integer(2)]);
let row2 = Row::from_values(vec![Value::integer(3), Value::integer(4)]);
let combined = row1.concat(&row2);
assert_eq!(combined.len(), 4);
assert_eq!(combined[0], Value::integer(1));
assert_eq!(combined[3], Value::integer(4));
}
#[test]
fn test_row_repeat() {
let row = Row::repeat(Value::integer(0), 5);
assert_eq!(row.len(), 5);
for v in row.iter() {
assert_eq!(*v, Value::integer(0));
}
}
#[test]
fn test_row_from_iterator() {
let row: Row = vec![Value::integer(1), Value::integer(2), Value::integer(3)]
.into_iter()
.collect();
assert_eq!(row.len(), 3);
}
#[test]
fn test_row_display() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::null(DataType::Text),
]);
assert_eq!(row.to_string(), "(1, hello, NULL)");
let empty = Row::new();
assert_eq!(empty.to_string(), "()");
}
#[test]
fn test_row_clone_subset() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::float(3.5),
]);
let subset = row.clone_subset(&[2, 0]);
assert_eq!(subset.len(), 2);
assert_eq!(subset[0], Value::float(3.5));
assert_eq!(subset[1], Value::integer(1));
}
#[test]
fn test_row_equality() {
let row1 = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let row2 = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let row3 = Row::from_values(vec![Value::integer(1), Value::text("world")]);
assert_eq!(row1, row2);
assert_ne!(row1, row3);
}
#[test]
fn test_row_into_values() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let values = row.into_values();
assert_eq!(values.len(), 2);
assert_eq!(values[0], Value::integer(1));
}
}