use crate::dtype::DataType;
use crate::error::{AxionError, AxionResult};
use super::core::Series;
use super::interface::SeriesTrait;
use std::any::Any;
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display};
#[derive(Clone)]
pub struct ListSeries {
name: String,
data: Vec<Option<Box<dyn SeriesTrait>>>,
inner_dtype: DataType,
}
impl ListSeries {
pub fn new(name: String, data: Vec<Option<Box<dyn SeriesTrait>>>, inner_dtype: DataType) -> Self {
ListSeries { name, data, inner_dtype }
}
pub fn get_inner_series(&self, index: usize) -> Option<&dyn SeriesTrait> {
self.data.get(index).and_then(|opt_box| opt_box.as_deref())
}
}
impl SeriesTrait for ListSeries {
fn name(&self) -> &str {
&self.name
}
fn dtype(&self) -> DataType {
DataType::List(Box::new(self.inner_dtype.clone()))
}
fn len(&self) -> usize {
self.data.len()
}
fn is_empty(&self) -> bool {
self.data.is_empty()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn clone_box(&self) -> Box<dyn SeriesTrait> {
Box::new(self.clone())
}
fn get_str(&self, index: usize) -> Option<String> {
self.data.get(index).and_then(|opt_box| {
opt_box.as_ref().map(|inner_series| {
if inner_series.is_empty() {
return "[]".to_string();
}
let max_elements_to_show = 5; let mut elements_str = Vec::with_capacity(max_elements_to_show);
for i in 0..std::cmp::min(inner_series.len(), max_elements_to_show) {
elements_str.push(inner_series.get_str(i).unwrap_or_else(|| "null".to_string()));
}
let mut result = format!("[{}", elements_str.join(", "));
if inner_series.len() > max_elements_to_show {
result.push_str(", ...");
}
result.push(']');
result
})
})
}
fn slice(&self, start: usize, length: usize) -> Box<dyn SeriesTrait> {
let end = std::cmp::min(start + length, self.len());
let start = std::cmp::min(start, end);
let sliced_data = self.data[start..end].to_vec(); Box::new(ListSeries::new(self.name.clone(), sliced_data, self.inner_dtype.clone()))
}
fn filter(&self, mask: &Series<bool>) -> AxionResult<Box<dyn SeriesTrait>> {
if mask.len() != self.len() {
return Err(AxionError::MismatchedLengths {
expected: self.len(),
found: mask.len(),
name: format!("filter mask for list series '{}'", self.name),
});
}
let mut new_data = Vec::with_capacity(self.len());
for (opt_val, opt_mask) in self.data.iter().zip(mask.data_internal().iter()) {
if let Some(true) = opt_mask {
new_data.push(opt_val.clone());
}
}
Ok(Box::new(ListSeries::new(self.name.clone(), new_data, self.inner_dtype.clone())))
}
fn take_indices(&self, indices: &[usize]) -> AxionResult<Box<dyn SeriesTrait>> {
let mut new_data = Vec::with_capacity(indices.len());
for &idx in indices {
let opt_val = self.data.get(idx)
.ok_or_else(|| AxionError::IndexOutOfBounds(idx, self.len()))?
.clone();
new_data.push(opt_val);
}
Ok(Box::new(ListSeries::new(self.name.clone(), new_data, self.inner_dtype.clone())))
}
fn take_indices_option(&self, indices: &[Option<usize>]) -> AxionResult<Box<dyn SeriesTrait>> {
let mut new_data = Vec::with_capacity(indices.len());
for opt_idx in indices {
match opt_idx {
Some(idx) => {
let opt_val = self.data.get(*idx)
.ok_or_else(|| AxionError::IndexOutOfBounds(*idx, self.len()))?
.clone();
new_data.push(opt_val);
}
None => {
new_data.push(None);
}
}
}
Ok(Box::new(ListSeries::new(self.name.clone(), new_data, self.inner_dtype.clone())))
}
fn rename(&mut self, new_name: &str) {
self.name = new_name.to_string();
}
fn series_equal(&self, other: &dyn SeriesTrait) -> bool {
if let Some(other_list) = other.as_any().downcast_ref::<ListSeries>() {
if self.inner_dtype != other_list.inner_dtype {
return false;
}
if self.len() != other_list.len() {
return false;
}
self.data.iter().zip(other_list.data.iter()).all(|(self_opt_box, other_opt_box)| {
match (self_opt_box, other_opt_box) {
(None, None) => true,
(Some(self_inner_series), Some(other_inner_series)) => {
self_inner_series.series_equal(&**other_inner_series)
}
_ => false,
}
})
} else {
false
}
}
fn compare_row(&self, _a_idx: usize, _b_idx: usize) -> Ordering {
Ordering::Equal
}
fn get_as_f64(&self, index: usize) -> AxionResult<Option<f64>> {
let _index = index; Ok(None)
}
fn is_null_at(&self, index: usize) -> bool {
self.data.get(index).map_or(true, |opt_val_ref| opt_val_ref.is_none())
}
}
impl Debug for ListSeries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ListSeries")
.field("name", &self.name)
.field("dtype", &self.dtype()) .field("len", &self.len())
.field("data_head", &self.data.iter().take(5).collect::<Vec<_>>()) .finish()
}
}
impl Display for ListSeries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ListSeries: '{}' [{:?}]", self.name, self.dtype())?;
for (i, opt_val) in self.data.iter().take(10).enumerate() {
write!(f, "{}: ", i)?;
match opt_val {
Some(val) => writeln!(f, "{:?}", val)?, None => writeln!(f, "null")?,
}
}
if self.len() > 10 {
writeln!(f, "... ({} more)", self.len() - 10)?;
}
Ok(())
}
}
pub fn new_list_series(name: String, data: Vec<Box<dyn SeriesTrait>>) -> AxionResult<ListSeries> {
let inner_dtype = data.first()
.map(|s| s.dtype())
.unwrap_or(DataType::Null);
for series in data.iter().skip(1) {
if series.dtype() != inner_dtype {
return Err(AxionError::Other(format!(
"List series '{}' expects inner type {:?} but found {:?}",
name, inner_dtype, series.dtype()
)));
}
}
let data_opts = data.into_iter().map(Some).collect();
Ok(ListSeries::new(name, data_opts, inner_dtype))
}