pub use self::error::{MergeError, ParseError};
use self::parser::Parser;
use self::section::Sections;
use super::reader::Error as ReadError;
use super::{Reader, Record, RecordKind};
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::fmt;
use std::path::Path;
#[macro_use]
mod parser;
mod error;
pub mod section;
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct Report {
pub sections: Sections,
}
impl Report {
pub fn new() -> Self {
Self::default()
}
pub fn from_reader<I, E>(iter: I) -> Result<Self, ParseError>
where
I: IntoIterator<Item = Result<Record, E>>,
E: Into<ReadError>,
{
let mut parser = Parser::new(iter.into_iter().map(|item| item.map_err(Into::into)));
let report = Report {
sections: section::parse(&mut parser)?,
};
Ok(report)
}
pub fn from_file<P>(path: P) -> Result<Self, ParseError>
where
P: AsRef<Path>,
{
let reader = Reader::open_file(path)
.map_err(ReadError::Io)
.map_err(ParseError::Read)?;
Self::from_reader(reader)
}
pub fn merge(&mut self, other: Self) -> Result<(), MergeError> {
self.sections.merge(other.sections)
}
pub fn merge_lossy(&mut self, other: Self) {
self.sections.merge_lossy(other.sections)
}
pub fn into_records(self) -> IntoRecords {
IntoRecords {
iter: section::into_records(self.sections),
}
}
}
pub struct IntoRecords {
iter: Box<dyn Iterator<Item = Record>>,
}
impl fmt::Debug for IntoRecords {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "IntoIter {{ .. }}")
}
}
impl Iterator for IntoRecords {
type Item = Record;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
trait Merge {
fn merge(&mut self, other: Self) -> Result<(), MergeError>;
fn merge_lossy(&mut self, other: Self);
}
impl<K, V> Merge for BTreeMap<K, V>
where
K: Ord,
V: Merge,
{
fn merge(&mut self, other: Self) -> Result<(), MergeError> {
for (key, value) in other {
match self.entry(key) {
Entry::Vacant(e) => {
let _ = e.insert(value);
}
Entry::Occupied(mut e) => e.get_mut().merge(value)?,
}
}
Ok(())
}
fn merge_lossy(&mut self, other: Self) {
for (key, value) in other {
match self.entry(key) {
Entry::Vacant(e) => {
let _ = e.insert(value);
}
Entry::Occupied(mut e) => e.get_mut().merge_lossy(value),
}
}
}
}