use std::io::{BufRead, Lines};
use serde::de::DeserializeOwned;
use crate::{Asciicast, Error, versions::Streamable};
pub struct Reader<V: Streamable, R: BufRead> {
header: V::Header,
lines: Lines<R>,
}
impl<V: Streamable, R: BufRead> Reader<V, R>
where
V::Header: DeserializeOwned,
{
pub fn open(reader: R) -> Result<Self, Error> {
let mut lines = reader.lines();
let header_line = loop {
match lines.next() {
Some(line) => {
let line = line?;
if line.trim().is_empty() {
continue;
}
break line;
}
None => return Err(Error::MissingHeader),
}
};
let header: V::Header = serde_json::from_str(&header_line)?;
let found = V::header_version(&header);
if found != V::NUMBER {
return Err(Error::VersionMismatch {
expected: V::NUMBER,
found,
});
}
Ok(Self { header, lines })
}
#[must_use]
pub fn header(&self) -> &V::Header {
&self.header
}
pub fn into_recording(mut self) -> Result<Asciicast<V>, Error> {
let events = (&mut self).collect::<Result<Vec<_>, _>>()?;
Ok(Asciicast {
header: self.header,
events,
})
}
pub fn absolute_times(self) -> impl Iterator<Item = Result<(f64, V::Event), Error>> {
self.scan(0.0_f64, |elapsed, event| {
Some(event.map(|event| {
let raw = V::event_time(&event);
let absolute = if V::RELATIVE_TIMING {
*elapsed += raw;
*elapsed
} else {
raw
};
(absolute, event)
}))
})
}
}
impl<V: Streamable, R: BufRead> Iterator for Reader<V, R> {
type Item = Result<V::Event, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.lines.find_map(|line| {
let line = match line {
Ok(line) => line,
Err(error) => return Some(Err(error.into())),
};
let trimmed = line.trim();
if trimmed.is_empty() || (V::SKIP_COMMENTS && trimmed.starts_with('#')) {
None
} else {
Some(V::parse_event(&line))
}
})
}
}