pub trait Data {
fn at(&self, row: usize, cell: usize) -> String;
fn rows(&self) -> usize;
fn columns(&self) -> usize;
}
#[derive(Debug, Clone)]
pub struct StringData {
rows: Vec<Vec<String>>,
columns: usize,
}
impl StringData {
pub fn new(rows: Vec<Vec<String>>) -> Self {
let columns = rows.iter().map(|row| row.len()).max().unwrap_or(0);
Self { rows, columns }
}
pub fn empty() -> Self {
Self {
rows: Vec::new(),
columns: 0,
}
}
pub fn append(&mut self, row: Vec<String>) {
self.columns = self.columns.max(row.len());
self.rows.push(row);
}
pub fn item(mut self, row: Vec<String>) -> Self {
self.append(row);
self
}
}
impl Data for StringData {
fn at(&self, row: usize, cell: usize) -> String {
if row >= self.rows.len() || cell >= self.rows[row].len() {
return String::new();
}
self.rows[row][cell].clone()
}
fn rows(&self) -> usize {
self.rows.len()
}
fn columns(&self) -> usize {
self.columns
}
}
pub struct Filter<D: Data> {
data: D,
filter: Option<Box<dyn Fn(usize) -> bool>>,
}
impl<D: Data> Filter<D> {
pub fn new(data: D) -> Self {
Self { data, filter: None }
}
pub fn filter<F>(mut self, f: F) -> Self
where
F: Fn(usize) -> bool + 'static,
{
self.filter = Some(Box::new(f));
self
}
}
impl<D: Data> Data for Filter<D> {
fn at(&self, row: usize, cell: usize) -> String {
if let Some(ref filter) = self.filter {
let mut j = 0;
for i in 0..self.data.rows() {
if filter(i) {
if j == row {
return self.data.at(i, cell);
}
j += 1;
}
}
String::new()
} else {
self.data.at(row, cell)
}
}
fn rows(&self) -> usize {
if let Some(ref filter) = self.filter {
let mut count = 0;
for i in 0..self.data.rows() {
if filter(i) {
count += 1;
}
}
count
} else {
self.data.rows()
}
}
fn columns(&self) -> usize {
self.data.columns()
}
}
pub fn data_to_matrix<D: Data + ?Sized>(data: &D) -> Vec<Vec<String>> {
let num_rows = data.rows();
let num_cols = data.columns();
let mut rows = Vec::with_capacity(num_rows);
for i in 0..num_rows {
let mut row = Vec::with_capacity(num_cols);
for j in 0..num_cols {
row.push(data.at(i, j));
}
rows.push(row);
}
rows
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_data_basic() {
let data = StringData::new(vec![
vec!["A".to_string(), "B".to_string()],
vec!["C".to_string(), "D".to_string(), "E".to_string()],
]);
assert_eq!(data.rows(), 2);
assert_eq!(data.columns(), 3); assert_eq!(data.at(0, 0), "A");
assert_eq!(data.at(0, 1), "B");
assert_eq!(data.at(1, 2), "E");
assert_eq!(data.at(0, 2), ""); assert_eq!(data.at(2, 0), ""); }
#[test]
fn test_string_data_append() {
let mut data = StringData::empty();
assert_eq!(data.rows(), 0);
assert_eq!(data.columns(), 0);
data.append(vec!["A".to_string(), "B".to_string()]);
assert_eq!(data.rows(), 1);
assert_eq!(data.columns(), 2);
data.append(vec!["C".to_string()]);
assert_eq!(data.rows(), 2);
assert_eq!(data.columns(), 2); }
#[test]
fn test_string_data_builder() {
let data = StringData::empty()
.item(vec!["Name".to_string(), "Age".to_string()])
.item(vec!["Alice".to_string(), "30".to_string()])
.item(vec![
"Bob".to_string(),
"25".to_string(),
"Engineer".to_string(),
]);
assert_eq!(data.rows(), 3);
assert_eq!(data.columns(), 3);
assert_eq!(data.at(1, 0), "Alice");
assert_eq!(data.at(2, 2), "Engineer");
}
#[test]
fn test_filter_basic() {
let data = StringData::new(vec![
vec!["A".to_string(), "1".to_string()],
vec!["B".to_string(), "2".to_string()],
vec!["C".to_string(), "3".to_string()],
vec!["D".to_string(), "4".to_string()],
]);
let filtered = Filter::new(data).filter(|row| row % 2 == 0);
assert_eq!(filtered.rows(), 2);
assert_eq!(filtered.columns(), 2);
assert_eq!(filtered.at(0, 0), "A"); assert_eq!(filtered.at(1, 0), "C"); assert_eq!(filtered.at(0, 1), "1");
assert_eq!(filtered.at(1, 1), "3");
}
#[test]
fn test_data_to_matrix() {
let data = StringData::new(vec![
vec!["A".to_string(), "B".to_string()],
vec!["C".to_string()],
]);
let matrix = data_to_matrix(&data);
assert_eq!(matrix.len(), 2);
assert_eq!(matrix[0], vec!["A", "B"]);
assert_eq!(matrix[1], vec!["C", ""]); }
}