diffy_fork_filenames/patch/
format.rs

1use super::{Hunk, Line, Patch, NO_NEWLINE_AT_EOF};
2use nu_ansi_term::{Color, Style};
3use std::{
4    fmt::{Display, Formatter, Result},
5    io,
6};
7
8/// Struct used to adjust the formatting of a `Patch`
9#[derive(Debug)]
10pub struct PatchFormatter {
11    with_color: bool,
12
13    context: Style,
14    delete: Style,
15    insert: Style,
16    hunk_header: Style,
17    patch_header: Style,
18    function_context: Style,
19}
20
21impl PatchFormatter {
22    /// Construct a new formatter
23    pub fn new() -> Self {
24        Self {
25            with_color: false,
26
27            context: Style::new(),
28            delete: Color::Red.normal(),
29            insert: Color::Green.normal(),
30            hunk_header: Color::Cyan.normal(),
31            patch_header: Style::new().bold(),
32            function_context: Style::new(),
33        }
34    }
35
36    /// Enable formatting a patch with color
37    pub fn with_color(mut self) -> Self {
38        self.with_color = true;
39        self
40    }
41
42    /// Returns a `Display` impl which can be used to print a Patch
43    pub fn fmt_patch<'a>(&'a self, patch: &'a Patch<'a, str>) -> impl Display + 'a {
44        PatchDisplay { f: self, patch }
45    }
46
47    pub fn write_patch_into<T: ToOwned + AsRef<[u8]> + ?Sized, W: io::Write>(
48        &self,
49        patch: &Patch<'_, T>,
50        w: W,
51    ) -> io::Result<()> {
52        PatchDisplay { f: self, patch }.write_into(w)
53    }
54
55    fn fmt_hunk<'a>(&'a self, hunk: &'a Hunk<'a, str>) -> impl Display + 'a {
56        HunkDisplay { f: self, hunk }
57    }
58
59    fn write_hunk_into<T: AsRef<[u8]> + ?Sized, W: io::Write>(
60        &self,
61        hunk: &Hunk<'_, T>,
62        w: W,
63    ) -> io::Result<()> {
64        HunkDisplay { f: self, hunk }.write_into(w)
65    }
66
67    fn fmt_line<'a>(&'a self, line: &'a Line<'a, str>) -> impl Display + 'a {
68        LineDisplay { f: self, line }
69    }
70
71    fn write_line_into<T: AsRef<[u8]> + ?Sized, W: io::Write>(
72        &self,
73        line: &Line<'_, T>,
74        w: W,
75    ) -> io::Result<()> {
76        LineDisplay { f: self, line }.write_into(w)
77    }
78}
79
80impl Default for PatchFormatter {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86struct PatchDisplay<'a, T: ToOwned + ?Sized> {
87    f: &'a PatchFormatter,
88    patch: &'a Patch<'a, T>,
89}
90
91impl<T: ToOwned + AsRef<[u8]> + ?Sized> PatchDisplay<'_, T> {
92    fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
93        if self.patch.original.is_some() || self.patch.modified.is_some() {
94            if self.f.with_color {
95                write!(w, "{}", self.f.patch_header.prefix())?;
96            }
97            if let Some(original) = &self.patch.original {
98                write!(w, "--- ")?;
99                original.write_into(&mut w)?;
100                writeln!(w)?;
101            }
102            if let Some(modified) = &self.patch.modified {
103                write!(w, "+++ ")?;
104                modified.write_into(&mut w)?;
105                writeln!(w)?;
106            }
107            if self.f.with_color {
108                write!(w, "{}", self.f.patch_header.suffix())?;
109            }
110        }
111
112        for hunk in &self.patch.hunks {
113            self.f.write_hunk_into(hunk, &mut w)?;
114        }
115
116        Ok(())
117    }
118}
119
120impl Display for PatchDisplay<'_, str> {
121    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
122        if self.patch.original.is_some() || self.patch.modified.is_some() {
123            if self.f.with_color {
124                write!(f, "{}", self.f.patch_header.prefix())?;
125            }
126            if let Some(original) = &self.patch.original {
127                writeln!(f, "--- {}", original)?;
128            }
129            if let Some(modified) = &self.patch.modified {
130                writeln!(f, "+++ {}", modified)?;
131            }
132            if self.f.with_color {
133                write!(f, "{}", self.f.patch_header.suffix())?;
134            }
135        }
136
137        for hunk in &self.patch.hunks {
138            write!(f, "{}", self.f.fmt_hunk(hunk))?;
139        }
140
141        Ok(())
142    }
143}
144
145struct HunkDisplay<'a, T: ?Sized> {
146    f: &'a PatchFormatter,
147    hunk: &'a Hunk<'a, T>,
148}
149
150impl<T: AsRef<[u8]> + ?Sized> HunkDisplay<'_, T> {
151    fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
152        if self.f.with_color {
153            write!(w, "{}", self.f.hunk_header.prefix())?;
154        }
155        write!(w, "@@ -{} +{} @@", self.hunk.old_range, self.hunk.new_range)?;
156        if self.f.with_color {
157            write!(w, "{}", self.f.hunk_header.suffix())?;
158        }
159
160        if let Some(ctx) = self.hunk.function_context {
161            write!(w, " ")?;
162            if self.f.with_color {
163                write!(w, "{}", self.f.function_context.prefix())?;
164            }
165            write!(w, " ")?;
166            w.write_all(ctx.as_ref())?;
167            if self.f.with_color {
168                write!(w, "{}", self.f.function_context.suffix())?;
169            }
170        }
171        writeln!(w)?;
172
173        for line in &self.hunk.lines {
174            self.f.write_line_into(line, &mut w)?;
175        }
176
177        Ok(())
178    }
179}
180
181impl Display for HunkDisplay<'_, str> {
182    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
183        if self.f.with_color {
184            write!(f, "{}", self.f.hunk_header.prefix())?;
185        }
186        write!(f, "@@ -{} +{} @@", self.hunk.old_range, self.hunk.new_range)?;
187        if self.f.with_color {
188            write!(f, "{}", self.f.hunk_header.suffix())?;
189        }
190
191        if let Some(ctx) = self.hunk.function_context {
192            write!(f, " ")?;
193            if self.f.with_color {
194                write!(f, "{}", self.f.function_context.prefix())?;
195            }
196            write!(f, " {}", ctx)?;
197            if self.f.with_color {
198                write!(f, "{}", self.f.function_context.suffix())?;
199            }
200        }
201        writeln!(f)?;
202
203        for line in &self.hunk.lines {
204            write!(f, "{}", self.f.fmt_line(line))?;
205        }
206
207        Ok(())
208    }
209}
210
211struct LineDisplay<'a, T: ?Sized> {
212    f: &'a PatchFormatter,
213    line: &'a Line<'a, T>,
214}
215
216impl<T: AsRef<[u8]> + ?Sized> LineDisplay<'_, T> {
217    fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
218        let (sign, line, style) = match self.line {
219            Line::Context(line) => (' ', line.as_ref(), self.f.context),
220            Line::Delete(line) => ('-', line.as_ref(), self.f.delete),
221            Line::Insert(line) => ('+', line.as_ref(), self.f.insert),
222        };
223
224        if self.f.with_color {
225            write!(w, "{}", style.prefix())?;
226        }
227
228        if sign == ' ' && line == b"\n" {
229            w.write_all(line)?;
230        } else {
231            write!(w, "{}", sign)?;
232            w.write_all(line)?;
233        }
234
235        if self.f.with_color {
236            write!(w, "{}", style.suffix())?;
237        }
238
239        if !line.ends_with(b"\n") {
240            writeln!(w)?;
241            writeln!(w, "{}", NO_NEWLINE_AT_EOF)?;
242        }
243
244        Ok(())
245    }
246}
247
248impl Display for LineDisplay<'_, str> {
249    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
250        let (sign, line, style) = match self.line {
251            Line::Context(line) => (' ', line, self.f.context),
252            Line::Delete(line) => ('-', line, self.f.delete),
253            Line::Insert(line) => ('+', line, self.f.insert),
254        };
255
256        if self.f.with_color {
257            write!(f, "{}", style.prefix())?;
258        }
259
260        if sign == ' ' && *line == "\n" {
261            write!(f, "{}", line)?;
262        } else {
263            write!(f, "{}{}", sign, line)?;
264        }
265
266        if self.f.with_color {
267            write!(f, "{}", style.suffix())?;
268        }
269
270        if !line.ends_with('\n') {
271            writeln!(f)?;
272            writeln!(f, "{}", NO_NEWLINE_AT_EOF)?;
273        }
274
275        Ok(())
276    }
277}