use crate::{header::Header, Error, NamingConvention, Result};
use flate2::read::GzDecoder;
use log::info;
use std::{
cell::RefCell,
fs::File,
io::{BufRead, BufReader, Lines, Read},
mem,
rc::Rc,
};
pub trait Sink {
fn insert(&mut self, _delta: usize) -> Result<()> {
Ok(())
}
fn delete(&mut self, _delta: usize) -> Result<()> {
Ok(())
}
fn end(&mut self, _delta: usize) -> Result<()> {
Ok(())
}
fn plain(&mut self, _text: &str, _keep: bool) -> Result<()> {
Ok(())
}
}
#[derive(Debug)]
pub enum Entry {
Insert { delta: usize },
Delete { delta: usize },
End { delta: usize },
Plain { text: String, keep: bool },
Control,
}
pub struct Parser<S: Sink, B> {
pull: PullParser<B>,
sink: Rc<RefCell<S>>,
pending: Option<String>,
lineno: usize,
}
impl<S: Sink> Parser<S, BufReader<Box<dyn Read>>> {
pub fn new(
naming: &dyn NamingConvention,
sink: S,
delta: usize,
) -> Result<Parser<S, BufReader<Box<dyn Read>>>> {
let rd = if naming.is_compressed() {
let fd = File::open(naming.main_file())?;
Box::new(GzDecoder::new(fd)) as Box<dyn Read>
} else {
Box::new(File::open(naming.main_file())?) as Box<dyn Read>
};
let lines = BufReader::new(rd).lines();
Parser::new_raw(lines, Rc::new(RefCell::new(sink)), delta)
}
}
impl<S: Sink, B: BufRead> Parser<S, B> {
pub fn new_raw(
source: Lines<B>,
sink: Rc<RefCell<S>>,
delta: usize,
) -> Result<Parser<S, B>> {
let pull = PullParser::new_raw(source, delta)?;
Ok(Parser {
pull,
sink,
pending: None,
lineno: 0,
})
}
pub fn parse_to(&mut self, lineno: usize) -> Result<usize> {
if let Some(text) = mem::replace(&mut self.pending, None) {
self.sink.borrow_mut().plain(&text, true)?;
}
loop {
match self.pull.next() {
Some(Ok(Entry::Plain { text, keep })) => {
if keep {
self.lineno += 1;
if self.lineno == lineno {
self.pending = Some(text);
return Ok(lineno);
}
}
self.sink.borrow_mut().plain(&text, keep)?;
}
Some(Ok(Entry::Insert { delta })) => {
self.sink.borrow_mut().insert(delta)?;
}
Some(Ok(Entry::Delete { delta })) => {
self.sink.borrow_mut().delete(delta)?;
}
Some(Ok(Entry::End { delta })) => {
self.sink.borrow_mut().end(delta)?;
}
Some(Ok(Entry::Control)) => (),
Some(Err(err)) => {
return Err(err);
}
None => {
return Ok(0);
}
}
}
}
pub fn get_header(&self) -> &Header {
&self.pull.header
}
pub fn into_header(self) -> Header {
self.pull.into_header()
}
pub fn get_sink(&self) -> Rc<RefCell<S>> {
self.sink.clone()
}
}
pub struct PullParser<B> {
source: Lines<B>,
delta: usize,
delta_state: Vec<OneDelta>,
keeping: bool,
header: Header,
}
impl PullParser<BufReader<Box<dyn Read>>> {
pub fn new(
naming: &dyn NamingConvention,
delta: usize,
) -> Result<PullParser<BufReader<Box<dyn Read>>>> {
let rd = if naming.is_compressed() {
let fd = File::open(naming.main_file())?;
Box::new(GzDecoder::new(fd)) as Box<dyn Read>
} else {
Box::new(File::open(naming.main_file())?) as Box<dyn Read>
};
let lines = BufReader::new(rd).lines();
PullParser::new_raw(lines, delta)
}
}
impl<B: BufRead> PullParser<B> {
pub fn new_raw(mut source: Lines<B>, delta: usize) -> Result<PullParser<B>> {
if let Some(line) = source.next() {
let line = line?;
let header = Header::decode(&line)?;
Ok(PullParser {
source,
delta,
delta_state: vec![],
keeping: false,
header,
})
} else {
Err(Error::EmptyWeave)
}
}
fn pop(&mut self, delta: usize) {
let pos = match self
.delta_state
.binary_search_by(|ent| delta.cmp(&ent.delta))
{
Ok(pos) => pos,
Err(_) => unreachable!(),
};
self.delta_state.remove(pos);
}
fn push(&mut self, delta: usize, mode: StateMode) {
match self
.delta_state
.binary_search_by(|ent| delta.cmp(&ent.delta))
{
Ok(_) => panic!("Duplicate state in push"),
Err(pos) => self.delta_state.insert(pos, OneDelta { delta, mode }),
}
}
fn update_keep(&mut self) {
info!("Update: {:?}", self.delta_state);
for st in &self.delta_state {
match st.mode {
StateMode::Keep => {
self.keeping = true;
return;
}
StateMode::Skip => {
self.keeping = false;
return;
}
_ => (),
}
}
self.keeping = false;
}
pub fn get_header(&self) -> &Header {
&self.header
}
pub fn into_header(self) -> Header {
self.header
}
}
impl<B: BufRead> Iterator for PullParser<B> {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Result<Entry>> {
let line = match self.source.next() {
None => return None,
Some(Ok(line)) => line,
Some(Err(e)) => return Some(Err(From::from(e))),
};
info!("line: {:?}", line);
let textual = match line.bytes().next() {
None => true,
Some(ch) if ch != b'\x01' => true,
_ => false,
};
if textual {
return Some(Ok(Entry::Plain {
text: line,
keep: self.keeping,
}));
}
let linebytes = line.as_bytes();
if linebytes.len() < 4 {
return Some(Ok(Entry::Control));
}
if linebytes[1] != b'I' && linebytes[1] != b'D' && linebytes[1] != b'E' {
return Some(Ok(Entry::Control));
};
let this_delta: usize = line[3..].parse().unwrap();
match linebytes[1] {
b'E' => {
self.pop(this_delta);
self.update_keep();
Some(Ok(Entry::End { delta: this_delta }))
}
b'I' => {
if self.delta >= this_delta {
self.push(this_delta, StateMode::Keep);
} else {
self.push(this_delta, StateMode::Skip);
}
self.update_keep();
Some(Ok(Entry::Insert { delta: this_delta }))
}
b'D' => {
if self.delta >= this_delta {
self.push(this_delta, StateMode::Skip);
} else {
self.push(this_delta, StateMode::Next);
}
self.update_keep();
Some(Ok(Entry::Delete { delta: this_delta }))
}
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum StateMode {
Keep,
Skip,
Next,
}
#[derive(Debug)]
struct OneDelta {
delta: usize,
mode: StateMode,
}