use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::Path;
const CSV_SEP: &str = ",";
type CsvCell = String;
type CsvHead = Vec<CsvCell>;
type CsvRow = Vec<CsvCell>;
#[derive(Debug, Default)]
pub struct CsvFile {
heads: CsvHead,
rows: Vec<CsvRow>,
}
impl CsvFile {
pub fn new() -> Self {
Self::default()
}
pub fn heads(&self) -> &CsvHead {
&self.heads
}
pub fn head_pos(&self, name: &str) -> Option<usize> {
self.heads.iter().position(|head| head.eq(name))
}
pub fn rows(&self) -> &Vec<CsvRow> {
&self.rows
}
pub fn cols(&self, name: &str) -> Option<CsvRow> {
let index = self.head_pos(name)?;
let cols = self
.rows
.iter()
.map(|row| {
if let Some(data) = row.get(index) {
data.to_string()
} else {
"".to_string()
}
})
.collect::<Vec<String>>();
Some(cols)
}
pub fn row(&self, position: usize) -> Option<&CsvRow> {
self.rows.get(position)
}
pub fn cell(&self, row: usize, col: usize) -> Option<&CsvCell> {
let row_data = self.rows.get(row)?;
row_data.get(col)
}
}
impl CsvFile {
pub fn push_head(&mut self, name: &str) {
self.heads.push(name.to_string());
}
pub fn set_head(&mut self, position: usize, name: &str) {
if let None = self.heads.get(position) {
return;
}
self.heads[position] = name.to_string();
}
pub fn insert_head(&mut self, position: usize, name: &str) {
self.heads.insert(position, name.to_string());
}
pub fn delete_head(&mut self, position: usize) -> String {
self.heads.remove(position)
}
pub fn pop_head(&mut self) -> Option<String> {
self.heads.pop()
}
pub fn push_col(&mut self, row: usize, value: &str) {
if let None = self.rows.get(row) {
return;
}
self.rows[row].push(value.to_string());
}
pub fn set_col(&mut self, row: usize, col: usize, value: &str) {
if let None = self.rows.get(row) {
return;
}
if let None = self.rows[row].get(col) {
return;
}
self.rows[row][col] = value.to_string();
}
pub fn insert_col(&mut self, row: usize, position: usize, value: &str) {
self.rows[row].insert(position, value.to_string())
}
pub fn delete_col(&mut self, position: usize) {
self.delete_head(position);
for row in self.rows.iter_mut() {
if let None = row.get(position) {
continue;
}
row.remove(position);
}
}
pub fn pop_col(&mut self) {
self.pop_head();
self.rows.iter_mut().for_each(|row| {
row.pop();
});
}
pub fn push_row(&mut self, value: &[&str]) {
self.rows.push(self.row_mapper(value));
}
pub fn set_row(&mut self, position: usize, value: &[&str]) {
if let None = self.rows.get(position) {
return;
}
self.rows[position] = self.row_mapper(value);
}
pub fn insert_row(&mut self, position: usize, value: &[&str]) {
self.rows.insert(position, self.row_mapper(value));
}
pub fn delete_row(&mut self, position: usize) -> Option<Vec<String>> {
if let None = self.rows.get(position) {
return None;
}
Some(self.rows.remove(position))
}
pub fn pop_row(&mut self) -> Option<Vec<String>> {
self.rows.pop()
}
fn row_mapper(&self, value: &[&str]) -> CsvRow {
value
.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<String>>()
}
}
impl CsvFile {
pub fn read<P: AsRef<Path>>(path: P) -> Result<CsvFile, io::Error> {
let file = File::open(path)?;
let buf = BufReader::new(file);
let contents = buf
.lines()
.flatten()
.filter(|line| !line.is_empty())
.map(|row| {
row.split(CSV_SEP)
.map(std::string::ToString::to_string)
.collect::<Vec<String>>()
})
.collect::<Vec<CsvRow>>();
Ok(CsvFile {
heads: contents[0].clone(),
rows: contents[1..].to_vec(),
})
}
pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), io::Error> {
let file = File::create(path)?;
let mut buf = BufWriter::new(file);
if !self.heads.is_empty() {
buf.write(self.heads().join(CSV_SEP).as_bytes())?;
buf.write("\n".as_bytes())?;
}
for row in self.rows().into_iter() {
buf.write(row.join(CSV_SEP).as_bytes())?;
buf.write("\n".as_bytes())?;
}
buf.flush()
}
}
impl fmt::Display for CsvFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for head in self.heads.iter() {
write!(f, "- {} -", "-".repeat(head.len()))?;
}
write!(f, "\n")?;
for head in self.heads.iter() {
write!(f, "- {} -", head)?;
}
write!(f, "\n")?;
for head in self.heads.iter() {
write!(f, "- {} -", "-".repeat(head.len()))?;
}
write!(f, "\n")?;
for row in self.rows.iter() {
for col in row.iter() {
write!(f, "- {} -", col)?;
}
write!(f, "\n")?;
}
write!(f, "")
}
}