use combine:: { parser, Parser, ParseError as CombinatorParseError };
use combine::primitives:: { Stream };
use record:: { LCOVRecord };
use combinator:: { record, report };
use std::fs:: { File };
use std::result:: { Result };
use std::io:: { Result as IOResult, Error as IOError, Read, BufRead, BufReader };
use std::path:: { Path };
use std::convert:: { From };
use std::fmt:: { Display, Formatter, Result as FormatResult };
use std::error::Error;
pub type ParseResult<T> = Result<T, RecordParseError>;
#[derive(PartialEq, Debug)]
pub struct RecordParseError {
pub line: u32,
pub column: u32,
pub message: String
}
impl<'a, P: Stream<Item=char, Range=&'a str>> From<CombinatorParseError<P>> for RecordParseError {
fn from(error: CombinatorParseError<P>) -> Self {
let line = error.position.line;
let column = error.position.column;
RecordParseError {
line: line as u32,
column: column as u32,
message: format!("{}", error)
}
}
}
impl Display for RecordParseError {
fn fmt(&self, f: &mut Formatter) -> FormatResult {
write!(f, "{}", self.message)
}
}
impl Error for RecordParseError {
fn description(&self) -> &str {
&self.message[..]
}
}
#[derive(Debug)]
pub enum ParseError {
IOError(IOError),
RecordParseError(RecordParseError)
}
impl From<IOError> for ParseError {
fn from(err: IOError) -> Self {
ParseError::IOError(err)
}
}
impl From<RecordParseError> for ParseError {
fn from(err: RecordParseError) -> Self {
ParseError::RecordParseError(err)
}
}
pub struct LCOVParser<T> {
line: u32,
reader: BufReader<T>
}
impl<T: Read> LCOVParser<T> {
pub fn new(reader: T) -> Self {
LCOVParser {
line: 0,
reader: BufReader::new(reader)
}
}
pub fn parse(&mut self) -> Result<Vec<LCOVRecord>, ParseError> {
let mut records = vec![];
loop {
let result = try!(self.next());
match result {
Some(record) => records.push(record),
None => { break; }
}
}
Ok(records)
}
pub fn next(&mut self) -> Result<Option<LCOVRecord>, ParseError> {
let mut line = String::new();
let size = try!(self.reader.read_line(&mut line));
if size <= 0 {
return Ok(None);
}
self.line += 1;
let record = try!(self.parse_record(line.as_str()));
return Ok( Some(record) );
}
fn parse_record(&mut self, line: &str) -> Result<LCOVRecord, RecordParseError> {
match parse_record(line) {
Ok(record) => Ok(record),
Err(err) => {
Err(RecordParseError {
line: self.line.clone(),
column: err.column,
message: err.message
})
}
}
}
}
pub trait FromFile<T> {
fn from_file<P: AsRef<Path>>(path: P) -> IOResult<LCOVParser<T>>;
}
impl FromFile<File> for LCOVParser<File> {
fn from_file<P: AsRef<Path>>(path: P) -> IOResult<LCOVParser<File>> {
let file = try!(File::open(path));
Ok(LCOVParser::new(file))
}
}
#[inline]
pub fn parse_record(input: &str) -> ParseResult<LCOVRecord> {
let (record, _) = try!(parser(record).parse(input));
Ok(record)
}
#[inline]
pub fn parse_report(input: &str) -> ParseResult<Vec<LCOVRecord>> {
let (records, _) = try!(parser(report).parse(input));
Ok(records)
}