1use combine:: { parser, Parser, ParseError as CombinatorParseError, State };
12use record:: { LCOVRecord };
13use combinator:: { record, report };
14use std::fs:: { File };
15use std::result:: { Result };
16use std::io:: { Result as IOResult, Error as IOError, Read, BufRead, BufReader };
17use std::path:: { Path };
18use std::convert:: { From };
19use std::fmt:: { Display, Formatter, Result as FormatResult };
20use std::error::Error;
21
22pub type ParseResult<T> = Result<T, RecordParseError>;
23
24#[derive(PartialEq, Debug)]
25pub struct RecordParseError {
26 pub line: u32,
27 pub column: u32,
28 pub message: String
29}
30
31impl<'a> From<CombinatorParseError<State<&'a str>>> for RecordParseError {
32 fn from(error: CombinatorParseError<State<&'a str>>) -> Self {
33 let line = error.position.line;
34 let column = error.position.column;
35
36 RecordParseError {
37 line: line as u32,
38 column: column as u32,
39 message: format!("{}", error)
40 }
41 }
42}
43
44impl Display for RecordParseError {
45 fn fmt(&self, f: &mut Formatter) -> FormatResult {
46 write!(f, "{}", self.message)
47 }
48}
49
50impl Error for RecordParseError {
51 fn description(&self) -> &str {
52 &self.message[..]
53 }
54}
55
56#[derive(Debug)]
57pub enum ParseError {
58 IOError(IOError),
59 RecordParseError(RecordParseError)
60}
61
62impl From<IOError> for ParseError {
63 fn from(err: IOError) -> Self {
64 ParseError::IOError(err)
65 }
66}
67impl From<RecordParseError> for ParseError {
68 fn from(err: RecordParseError) -> Self {
69 ParseError::RecordParseError(err)
70 }
71}
72
73impl Error for ParseError {
74 fn description(&self) -> &str {
75 match self {
76 &ParseError::IOError(ref err) => err.description(),
77 &ParseError::RecordParseError(ref err) => err.description(),
78 }
79 }
80}
81
82impl Display for ParseError {
83 fn fmt(&self, formatter: &mut Formatter) -> FormatResult {
84 match self {
85 &ParseError::IOError(ref err) => err.fmt(formatter),
86 &ParseError::RecordParseError(ref err) => err.fmt(formatter),
87 }
88 }
89}
90
91pub struct LCOVParser<T> {
115 line: u32,
116 reader: BufReader<T>
117}
118
119impl<T: Read> LCOVParser<T> {
120 pub fn new(reader: T) -> Self {
121 LCOVParser {
122 line: 0,
123 reader: BufReader::new(reader)
124 }
125 }
126 pub fn parse(&mut self) -> Result<Vec<LCOVRecord>, ParseError> {
127 let mut records = vec![];
128 loop {
129 let result = self.next()?;
130 match result {
131 Some(record) => records.push(record),
132 None => { break; }
133 }
134 }
135 Ok(records)
136 }
137 pub fn next(&mut self) -> Result<Option<LCOVRecord>, ParseError> {
138 let mut line = String::new();
139 let size = self.reader.read_line(&mut line)?;
140 if size <= 0 {
141 return Ok(None);
142 }
143 self.line += 1;
144 let record = self.parse_record(line.as_str())?;
145 return Ok( Some(record) );
146 }
147 fn parse_record(&mut self, line: &str) -> Result<LCOVRecord, RecordParseError> {
148 match parse_record(line) {
149 Ok(record) => Ok(record),
150 Err(err) => {
151 Err(RecordParseError {
152 line: self.line.clone(),
153 column: err.column,
154 message: err.message
155 })
156 }
157 }
158 }
159}
160
161pub trait FromFile<T> {
162 fn from_file<P: AsRef<Path>>(path: P) -> IOResult<LCOVParser<T>>;
163}
164
165impl FromFile<File> for LCOVParser<File> {
178 fn from_file<P: AsRef<Path>>(path: P) -> IOResult<LCOVParser<File>> {
179 let file = File::open(path)?;
180 Ok(LCOVParser::new(file))
181 }
182}
183
184#[inline]
197pub fn parse_record(input: &str) -> ParseResult<LCOVRecord> {
198 let (record, _) = parser(record).parse(State::new(input))?;
199 Ok(record)
200}
201
202#[inline]
217pub fn parse_report(input: &str) -> ParseResult<Vec<LCOVRecord>> {
218 let (records, _) = parser(report).parse(State::new(input))?;
219 Ok(records)
220}