use std::ops::Index;
use crate::{AkitaDataError, ColumnIndex, AkitaValue, FromAkitaValue, from_akita_value, from_akita_value_opt};
#[derive(Debug, PartialEq, Clone)]
pub struct Row {
pub columns: Vec<String>,
pub data: Vec<AkitaValue>,
}
impl Row {
pub fn new(columns: Vec<String>, data: Vec<AkitaValue>) -> Self {
Self { columns, data }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn columns_ref(&self) -> &[String] {
&self.columns
}
pub fn columns(&self) -> Vec<String> {
self.columns.clone()
}
pub fn as_ref(&self, index: usize) -> Option<&AkitaValue> {
self.data.get(index)
}
pub fn iter(&self) -> RowIter<'_> {
RowIter {
columns: &self.columns,
data: &self.data,
index: 0,
}
}
pub fn get<T, I>(&self, index: I) -> Option<T>
where
T: FromAkitaValue,
I: ColumnIndex,
{
index.idx(&*self.columns).and_then(|idx| {
self.data
.get(idx)
.map(|x| from_akita_value(x.clone()))
})
}
pub fn get_by_column<T>(&self, column: &str) -> Option<T>
where
T: FromAkitaValue,
{
self.get_value_by_column(column)
.map(|value| from_akita_value(value.clone()))
}
pub fn get_by_column_opt<T>(&self, column: &str) -> Option<Result<T, AkitaDataError>>
where
T: FromAkitaValue,
{
self.get_value_by_column(column)
.map(|value| from_akita_value_opt(value.clone()))
}
pub fn get_opt<T, I>(&self, index: I) -> Option<Result<T, AkitaDataError>>
where
T: FromAkitaValue,
I: ColumnIndex,
{
index
.idx(&*self.columns)
.and_then(|idx| self.data.get(idx))
.map(|x| from_akita_value_opt(x.clone()))
}
pub fn take<T, I>(&mut self, index: I) -> Option<T>
where
T: FromAkitaValue,
I: ColumnIndex,
{
index.idx(&*self.columns).and_then(|idx| {
self.data
.get_mut(idx)
.map(|x| x.take())
.map(from_akita_value::<T>)
})
}
pub fn take_by_column<T>(&mut self, column: &str) -> Option<T>
where
T: FromAkitaValue,
{
self.columns
.iter()
.position(|c| c == column)
.and_then(|idx| self.data.get_mut(idx))
.map(|value| value.take())
.map(from_akita_value::<T>)
}
pub fn take_opt<T, I>(&mut self, index: I) -> Option<Result<T, AkitaDataError>>
where
T: FromAkitaValue,
I: ColumnIndex,
{
index
.idx(&*self.columns)
.and_then(|idx| self.data.get_mut(idx))
.map(|x| x.take())
.map(from_akita_value_opt::<T>)
}
pub fn unwrap(self) -> Vec<AkitaValue> {
self.data
.into_iter()
.collect()
}
#[doc(hidden)]
pub fn place(&mut self, index: usize, value: AkitaValue) {
self.data[index] = value;
}
pub fn get_value(&self, index: usize) -> Option<&AkitaValue> {
self.data.get(index)
}
pub fn get_value_by_column(&self, column: &str) -> Option<&AkitaValue> {
self.columns
.iter()
.position(|c| c == column)
.and_then(|idx| self.data.get(idx))
}
pub fn as_object(&self) -> AkitaValue {
let mut object = AkitaValue::new_object();
for (column, value) in self.iter() {
object.insert_obj_value(column, value);
}
object
}
pub fn into_object(self) -> AkitaValue {
let mut object = AkitaValue::new_object();
for (column, value) in self.columns.into_iter().zip(self.data.into_iter()) {
object.insert_obj_value(&column, &value);
}
object
}
pub fn into_data(self) -> Vec<AkitaValue> {
self.data
}
pub fn set_value(&mut self, index: usize, value: AkitaValue) -> Result<(), AkitaDataError> {
if index < self.data.len() {
self.data[index] = value;
Ok(())
} else {
Err(AkitaDataError::IndexOutOfBounds(index, self.data.len()))
}
}
pub fn contains_column(&self, column: &str) -> bool {
self.columns.iter().any(|c| c == column)
}
}
impl Index<usize> for Row {
type Output = AkitaValue;
fn index(&self, index: usize) -> &AkitaValue {
&self.data[index]
}
}
impl<'a> Index<&'a str> for Row {
type Output = AkitaValue;
fn index(&self, column: &'a str) -> &AkitaValue {
for (i, col) in self.columns.iter().enumerate() {
if col == column {
return &self.data[i];
}
}
panic!("No such column: `{}` in row with columns: {:?}", column, self.columns);
}
}
pub struct RowIter<'a> {
columns: &'a [String],
data: &'a [AkitaValue],
index: usize,
}
impl<'a> Iterator for RowIter<'a> {
type Item = (&'a String, &'a AkitaValue);
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.columns.len() && self.index < self.data.len() {
let column = &self.columns[self.index];
let value = &self.data[self.index];
self.index += 1;
Some((column, value))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.columns.len().saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for RowIter<'a> {}