use regex::Regex;
use std::{
collections::BTreeMap,
fs::{remove_file, rename},
io::{self, BufRead, BufReader, BufWriter, Write},
mem::replace,
path::PathBuf,
process::{Command, Stdio},
};
use crate::{header::Header, Entry, Error, NamingConvention, Parser, PullParser, Result, Sink, WriterInfo};
pub struct DeltaWriter<'n> {
naming: &'n dyn NamingConvention,
temp: Option<WriterInfo>,
base: usize,
new_delta: usize,
base_name: PathBuf,
diff_re: Regex,
header: Header,
}
impl<'n> DeltaWriter<'n> {
pub fn new<'a, 'b, I>(nc: &dyn NamingConvention, tags: I, base: usize) -> Result<DeltaWriter>
where
I: Iterator<Item = (&'a str, &'b str)>,
{
let mut ntags = BTreeMap::new();
for (k, v) in tags {
ntags.insert(k.to_owned(), v.to_owned());
}
if !ntags.contains_key("name") {
return Err(Error::NameMissing);
}
let (base_name, mut base_file) = nc.temp_file()?;
let mut header = {
let mut parser = PullParser::new(nc, base)?;
for node in &mut parser {
match node? {
Entry::Plain { text, keep } => {
if keep {
writeln!(base_file, "{}", text)?;
}
}
_ => (),
}
}
parser.into_header()
};
let new_delta = header.add(ntags)?;
let (new_name, new_file) = nc.temp_file()?;
let new_info = WriterInfo {
name: new_name,
writer: Box::new(BufWriter::new(new_file)),
};
Ok(DeltaWriter {
naming: nc,
temp: Some(new_info),
base,
new_delta,
base_name,
diff_re: Regex::new(r"^(\d+)(,(\d+))?([acd]).*$").unwrap(),
header,
})
}
pub fn close(mut self) -> Result<()> {
let temp = replace(&mut self.temp, None);
let temp_name = match temp {
Some(mut wi) => {
wi.writer.flush()?;
drop(wi.writer);
wi.name
}
None => return Err(Error::AlreadyClosed),
};
let tweave_info = self.naming.new_temp()?;
let mut child = Command::new("diff")
.arg(self.base_name.as_os_str())
.arg(temp_name.as_os_str())
.stdout(Stdio::piped())
.spawn()?;
{
let lines = BufReader::new(child.stdout.as_mut().unwrap()).lines();
let weave_write = WeaveWriter {
dest: tweave_info.writer,
};
let mut parser = Parser::new(self.naming, weave_write, self.base)?;
let weave_write = parser.get_sink();
self.header.write(&mut weave_write.borrow_mut().dest)?;
let mut is_done = false;
let mut is_adding = false;
for line in lines {
let line = line?;
if let Some(cap) = self.diff_re.captures(&line) {
if is_adding {
weave_write.borrow_mut().end(self.new_delta)?;
is_adding = false;
}
let left = cap.get(1).unwrap().as_str().parse::<usize>().unwrap();
let right = match cap.get(3) {
None => left,
Some(r) => r.as_str().parse().unwrap(),
};
let cmd = cap.get(4).unwrap().as_str().chars().next().unwrap();
if cmd == 'd' || cmd == 'c' {
match parser.parse_to(left)? {
0 => return Err(Error::UnexpectedEof),
n if n == left => (),
_ => panic!("Unexpected parse result"),
}
weave_write.borrow_mut().delete(self.new_delta)?;
match parser.parse_to(right + 1) {
Ok(0) => is_done = true,
Ok(n) if n == right + 1 => (),
Ok(_) => panic!("Unexpected parse result"),
Err(e) => return Err(e),
}
weave_write.borrow_mut().end(self.new_delta)?;
} else {
match parser.parse_to(right + 1) {
Ok(0) => is_done = true,
Ok(n) if n == right + 1 => (),
Ok(_) => panic!("Unexpected parse result"),
Err(e) => return Err(e),
}
}
if cmd == 'c' || cmd == 'a' {
weave_write.borrow_mut().insert(self.new_delta)?;
is_adding = true;
}
continue;
}
match line.chars().next() {
None => panic!("Unexpected blank line in diff"),
Some('<') => continue,
Some('-') => continue,
Some('>') => {
weave_write.borrow_mut().plain(&line[2..], true)?;
}
Some(_) => panic!("Unexpected diff line: {:?}", line),
}
}
if is_adding {
weave_write.borrow_mut().end(self.new_delta)?;
}
if !is_done {
match parser.parse_to(0) {
Ok(0) => (),
Ok(_) => panic!("Unexpected non-eof"),
Err(e) => return Err(e),
}
}
}
match child.wait()?.code() {
None => return Err(Error::DiffKilled),
Some(0) => (), Some(1) => (), Some(n) => return Err(Error::DiffError(n)),
}
let _ = rename(self.naming.main_file(), self.naming.backup_file());
rename(tweave_info.name, self.naming.main_file())?;
remove_file(&self.base_name)?;
remove_file(&temp_name)?;
Ok(())
}
}
impl<'n> Write for DeltaWriter<'n> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.temp
.as_mut()
.expect("Attempt to write to DeltaWriter that is closed")
.writer
.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.temp
.as_mut()
.expect("Attempt to flush DeltaWriter that is closed")
.writer
.flush()
}
}
struct RevWriter<W: Write> {
dest: BufWriter<W>,
}
impl<W: Write> Sink for RevWriter<W> {
fn plain(&mut self, text: &str, keep: bool) -> Result<()> {
if !keep {
return Ok(());
}
writeln!(&mut self.dest, "{}", text)?;
Ok(())
}
}
struct WeaveWriter<W: Write> {
dest: W,
}
impl<W: Write> Sink for WeaveWriter<W> {
fn insert(&mut self, delta: usize) -> Result<()> {
writeln!(&mut self.dest, "\x01I {}", delta)?;
Ok(())
}
fn delete(&mut self, delta: usize) -> Result<()> {
writeln!(&mut self.dest, "\x01D {}", delta)?;
Ok(())
}
fn end(&mut self, delta: usize) -> Result<()> {
writeln!(&mut self.dest, "\x01E {}", delta)?;
Ok(())
}
fn plain(&mut self, text: &str, _keep: bool) -> Result<()> {
writeln!(&mut self.dest, "{}", text)?;
Ok(())
}
}