use std::fs::File;
use std::io::Read;
use std::mem::take;
use std::path::PathBuf;
use crate::cover::{parse_cover_line, Cover};
use crate::csv::{CsvReader, Sep};
use crate::header::{parse_header, Header};
use crate::Error;
pub struct FecFile {
reader: Option<Box<dyn Read + Send>>,
header: Option<Header>,
cover: Option<Cover>,
sep: Option<Sep>,
csv_reader: Option<CsvReader<Box<dyn Read + Send>>>,
}
impl FecFile {
pub fn from_reader(reader: Box<dyn Read + Send>) -> Self {
Self {
reader: Some(reader),
header: None,
cover: None,
sep: None,
csv_reader: None,
}
}
pub fn from_path(path: &PathBuf) -> Result<Self, Error> {
let file = File::open(path)?;
Ok(Self::from_reader(Box::new(file)))
}
pub fn get_header(&mut self) -> Result<&Header, Error> {
self.parse_header()?;
Ok(self.header.as_ref().expect("header should be set"))
}
pub fn get_cover(&mut self) -> Result<&Cover, Error> {
self.parse_cover()?;
Ok(self.cover.as_ref().expect("cover should be set"))
}
fn fec_version(&self) -> String {
self.header.as_ref().expect("No header").fec_version.clone()
}
pub fn next_line(&mut self) -> Option<Result<Vec<String>, Error>> {
match self.parse_cover() {
Err(e) => return Some(Err(e)),
Ok(_) => (),
}
let p = self.csv_reader.as_mut().expect("No row parser");
match p.next_line() {
None => return None,
Some(Ok(line)) => Some(Ok(line)),
Some(Err(e)) => return Some(Err(Error::RecordParseError(e.to_string()))),
}
}
pub fn lines(&mut self) -> LineIter {
LineIter { fec_file: self }
}
fn parse_header(&mut self) -> Result<(), Error> {
if self.header.is_some() {
return Ok(());
}
let reader = self.reader.as_mut().expect("no reader");
let header_parsing = parse_header(reader).map_err(Error::HeaderParseError)?;
self.header = Some(header_parsing.header.clone());
self.sep = Some(header_parsing.sep.clone());
Ok(())
}
fn parse_cover(&mut self) -> Result<(), Error> {
if self.cover.is_some() {
return Ok(());
}
self.make_csv_parser()?;
let fec_version = &self.fec_version().clone();
let p = self.csv_reader.as_mut().expect("No row parser");
let line = match p.next_line() {
None => return Err(Error::CoverParseError("no cover record".to_string())),
Some(Ok(record)) => record,
Some(Err(e)) => return Err(Error::CoverParseError(e.to_string())),
};
self.cover = Some(parse_cover_line(fec_version, &mut line.iter())?);
Ok(())
}
fn make_csv_parser(&mut self) -> Result<(), Error> {
if self.csv_reader.is_some() {
return Ok(());
}
self.parse_header()?;
let sep = self.sep.as_ref().expect("No sep");
if self.csv_reader.is_none() {
let reader = take(&mut self.reader).expect("no reader");
self.csv_reader = Some(CsvReader::new(reader, sep));
}
Ok(())
}
}
pub struct LineIter<'a> {
fec_file: &'a mut FecFile,
}
impl<'a> Iterator for LineIter<'a> {
type Item = Result<Vec<String>, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.fec_file.next_line()
}
}