use std::io::Read;
use csv::ReaderBuilder;
#[derive(Debug, Clone)]
pub enum Sep {
Comma,
Ascii28,
}
impl Sep {
pub fn to_byte(&self) -> u8 {
match self {
Sep::Comma => b',',
Sep::Ascii28 => b'\x1c',
}
}
pub fn detect(s: &[u8]) -> Self {
if s.contains(&b'\x1c') {
Self::Ascii28
} else {
Self::Comma
}
}
}
pub struct CsvReader<R: Read> {
records: csv::StringRecordsIntoIter<R>,
}
impl<R: Read> CsvReader<R> {
pub fn new(src: R, sep: &Sep) -> Self {
let reader = ReaderBuilder::new()
.delimiter(sep.to_byte())
.has_headers(false)
.flexible(true)
.from_reader(src);
Self {
records: reader.into_records(),
}
}
pub fn next_line(&mut self) -> Option<Result<Vec<String>, String>> {
let record_or_err = self.records.next()?;
log::debug!("raw_record: {:?}", record_or_err);
let strings: Vec<String> = match record_or_err {
Err(e) => return Some(Err(e.to_string())),
Ok(record) => record.iter().map(|s| s.to_string()).collect(),
};
Some(Ok(strings))
}
}