use NamingConvention;
use Result;
use flate2::FlateReadExt;
use header::Header;
use std::cell::RefCell;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Read};
use std::mem;
use std::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(())
}
}
pub struct Parser<S: Sink, B> {
source: Lines<B>,
sink: Rc<RefCell<S>>,
delta: usize,
delta_state: Vec<OneDelta>,
pending: Option<String>,
keeping: bool,
lineno: usize,
header: Header,
}
impl<S: Sink> Parser<S, BufReader<Box<Read>>> {
pub fn new(naming: &NamingConvention, sink: S, delta: usize)
-> Result<Parser<S, BufReader<Box<Read>>>>
{
let rd = if naming.is_compressed() {
let fd = File::open(naming.main_file())?;
Box::new(fd.gz_decode()?) as Box<Read>
} else {
Box::new(File::open(naming.main_file())?) as Box<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(mut source: Lines<B>, sink: Rc<RefCell<S>>, delta: usize) -> Result<Parser<S, B>> {
if let Some(line) = source.next() {
let line = line?;
let header = Header::from_str(&line)?;
Ok(Parser {
source: source,
sink: sink,
delta: delta,
delta_state: vec![],
pending: None,
keeping: false,
lineno: 0,
header: header,
})
} else {
Err("Weave file appears empty".into())
}
}
pub fn parse_to(&mut self, lineno: usize) -> Result<usize> {
if let Some(pending) = mem::replace(&mut self.pending, None) {
self.sink.borrow_mut().plain(&pending, self.keeping)?;
}
loop {
let line = match self.source.next() {
None => return Ok(0),
Some(line) => line?,
};
info!("line: {:?}", line);
let textual = match line.bytes().next() {
None => true,
Some(ch) if ch != b'\x01' => true,
_ => false,
};
if textual {
if self.keeping {
self.lineno += 1;
if self.lineno == lineno {
self.pending = Some(line);
return Ok(lineno);
}
}
info!("textual: keeping={}", self.keeping);
self.sink.borrow_mut().plain(&line, self.keeping)?;
continue;
}
let linebytes = line.as_bytes();
if linebytes.len() < 4 {
continue;
}
if linebytes[1] != b'I' && linebytes[1] != b'D' && linebytes[1] != b'E' {
continue;
}
let this_delta: usize = line[3..].parse()?;
match linebytes[1] {
b'E' => {
self.sink.borrow_mut().end(this_delta)?;
self.pop(this_delta);
}
b'I' => {
self.sink.borrow_mut().insert(this_delta)?;
if self.delta >= this_delta {
self.push(this_delta, StateMode::Keep);
} else {
self.push(this_delta, StateMode::Skip);
}
}
b'D' => {
self.sink.borrow_mut().delete(this_delta)?;
if self.delta >= this_delta {
self.push(this_delta, StateMode::Skip);
} else {
self.push(this_delta, StateMode::Next);
}
}
_ => unreachable!(),
}
self.update_keep();
}
}
fn pop(&mut self, delta: usize) {
let pos = match self.delta_state.binary_search_by(|ent| delta.cmp(&ent.delta)) {
Ok(pos) => pos,
Err(_) => panic!("State of pop not present"),
};
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: delta,
mode: 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
}
pub fn get_sink(&self) -> Rc<RefCell<S>> {
self.sink.clone()
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum StateMode { Keep, Skip, Next }
#[derive(Debug)]
struct OneDelta {
delta: usize,
mode: StateMode,
}