patchfile/
lib.rs

1extern crate diff;
2
3mod model;
4mod algo;
5
6use diff::Result as DiffDelta;
7use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
8use crate::algo::render_diff;
9use crate::model::{DiffU, LineNumber, Range};
10use std::io::Result as IoResult;
11
12/// Creates a builder for converting two files into a patchfile
13pub struct Patchfile<'a> {
14    remote_name: &'a str,
15    local_name: &'a str,
16    remote_time: Option<&'a str>,
17    local_time: Option<&'a str>,
18}
19
20impl<'a> Patchfile<'a> {
21    /// Sets the same name for both files
22    pub fn new(name: &'a str) -> Self {
23        Patchfile {
24            remote_name: name,
25            local_name: name,
26            remote_time: None,
27            local_time: None,
28        }
29    }
30
31    /// Sets different names for both files
32    pub fn with_names(remote_name: &'a str, local_name: &'a str) -> Self {
33        Patchfile {
34            remote_name,
35            local_name,
36            remote_time: None,
37            local_time: None,
38        }
39    }
40
41    /// Set the remote file's timestamp
42    pub fn remote_time(&'a mut self, remote_time: &'a str) -> &'a mut Self {
43        self.remote_time = Some(remote_time);
44
45        self
46    }
47
48    /// Set the local file's timestamp
49    pub fn local_time(&'a mut self, local_time: &'a str) -> &'a mut Self {
50        self.local_time = Some(local_time);
51
52        self
53    }
54
55    /// Write patch formatted text to stdout
56    pub fn print<'b>(&'a self, remote: &'b str, local: &'b str) -> IoResult<()> {
57        self.write_impl(StandardStream::stdout(ColorChoice::Auto), remote, local)
58    }
59
60    /// Write patch formatted text to stderr
61    pub fn eprint<'b>(&'a self, remote: &'b str, local: &'b str) -> IoResult<()> {
62        self.write_impl(StandardStream::stderr(ColorChoice::Auto), remote, local)
63    }
64
65    fn write_impl<W: WriteColor>(&self, mut w: W, remote: &str, local: &str) -> IoResult<()> {
66        let diff = diff::lines(local, remote);
67
68        w.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
69        writeln!(w, "--- {}\t{}", self.local_name, self.local_time.unwrap_or(""))?;
70        w.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
71        writeln!(w, "+++ {}\t{}", self.remote_name, self.remote_time.unwrap_or(""))?;
72
73        for d in render_diff(&diff) {
74            let s = d.to_string();
75            match d {
76                DiffU::CaretPos { .. } => {
77                    w.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))
78                }
79                DiffU::Addition(_) => {
80                    w.set_color(ColorSpec::new().set_fg(Some(Color::Green)))
81                }
82                DiffU::Deletion(_) => {
83                    w.set_color(ColorSpec::new().set_fg(Some(Color::Red)))
84                }
85                DiffU::Display(_) => {
86                    w.set_color(ColorSpec::new().set_fg(None))
87                }
88            }?;
89
90            writeln!(w, "{}", s)?;
91        };
92
93        Ok(())
94    }
95}
96
97#[cfg(test)]
98mod tests;