diffy_fork_filenames/patch/
format.rs1use 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#[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 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 pub fn with_color(mut self) -> Self {
38 self.with_color = true;
39 self
40 }
41
42 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}