brumby 0.7.3

Derivatives and multiples pricing for racing and sports.
//! Utilities for working with CSV files.

use std::borrow::Cow;
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader, BufWriter, Lines, Write};
use std::ops::{Index, IndexMut};
use std::path::Path;
use std::vec::IntoIter;

pub struct CsvWriter {
    writer: BufWriter<File>,
}
impl CsvWriter {
    pub fn create(path: impl AsRef<Path>) -> Result<Self, io::Error> {
        let file = File::create(path)?;
        let writer = BufWriter::new(file);
        Ok(Self { writer })
    }

    pub fn append<R>(&mut self, record: R) -> Result<(), io::Error>
    where
        R: IntoIterator,
        R::Item: AsRef<str>,
    {
        let mut first = true;
        for datum in record {
            if first {
                first = false;
            } else {
                self.writer.write_all(",".as_bytes())?;
            }
            let str: &str = datum.as_ref();
            self.writer.write_all(str.as_bytes())?;
        }
        self.writer.write_all("\n".as_bytes())?;
        Ok(())
    }

    pub fn flush(&mut self) -> Result<(), io::Error> {
        self.writer.flush()
    }
}

pub struct CsvReader {
    lines: Lines<BufReader<File>>,
}
impl CsvReader {
    pub fn open(path: impl AsRef<Path>) -> Result<Self, io::Error> {
        let file = File::open(path)?;
        let lines = BufReader::new(file).lines();
        Ok(Self { lines })
    }

    pub fn read(&mut self) -> Option<Result<Record, io::Error>> {
        self.lines
            .next()
            .map(|line| {
                line.map(|line| {
                    Record::with_values(line.split(',').collect::<Vec<_>>())
                })
            })
    }
}

impl Iterator for CsvReader {
    type Item = Result<Record, io::Error>;

    fn next(&mut self) -> Option<Self::Item> {
        self.read()
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Record {
    items: Vec<Cow<'static, str>>,
}
impl Record {
    pub fn with_capacity(capacity: usize) -> Self {
        let mut items = Vec::with_capacity(capacity);
        items.resize_with(capacity, || Cow::Borrowed(""));
        Self { items }
    }

    pub fn with_values<I>(values: I) -> Self
    where
        I: IntoIterator,
        I::Item: ToString,
    {
        let items = values
            .into_iter()
            .map(|value| Cow::Owned(value.to_string()))
            .collect();
        Self { items }
    }

    pub fn set(&mut self, ordinal: impl Into<usize>, value: &impl ToString) {
        self.items[ordinal.into()] = Cow::Owned(value.to_string());
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    pub fn is_empty(&self) -> bool { self.items.is_empty() }
}

impl IntoIterator for Record {
    type Item = Cow<'static, str>;
    type IntoIter = IntoIter<Cow<'static, str>>;

    fn into_iter(self) -> Self::IntoIter {
        self.items.into_iter()
    }
}

impl<I: Into<usize>> Index<I> for Record {
    type Output = Cow<'static, str>;

    fn index(&self, index: I) -> &Self::Output {
        &self.items[index.into()]
    }
}

impl<I: Into<usize>> IndexMut<I> for Record {
    fn index_mut(&mut self, index: I) -> &mut Self::Output {
        &mut self.items[index.into()]
    }
}