1use crate::{changestore::ChangeStore, pristine::*};
2
3pub const START_MARKER: &str = ">>>>>>>";
4
5pub const SEPARATOR: &str = "=======";
6
7pub const END_MARKER: &str = "<<<<<<<";
8
9pub trait VertexBuffer {
14 fn output_line<E, F>(&mut self, key: Vertex<ChangeId>, contents: F) -> Result<(), E>
15 where
16 E: From<std::io::Error>,
17 F: FnOnce(&mut [u8]) -> Result<(), E>;
18
19 fn output_conflict_marker<C: ChangeStore>(
20 &mut self,
21 s: &str,
22 id: usize,
23 sides: Option<(&C, &[&Hash])>,
24 ) -> Result<(), std::io::Error>;
25 fn begin_conflict<C: ChangeStore>(
26 &mut self,
27 id: usize,
28 side: Option<(&C, &[&Hash])>,
29 ) -> Result<(), std::io::Error> {
30 self.output_conflict_marker(START_MARKER, id, side)
31 }
32 fn begin_zombie_conflict<C: ChangeStore>(
33 &mut self,
34 id: usize,
35 add_del: Option<(&C, &[&Hash])>,
36 ) -> Result<(), std::io::Error> {
37 self.output_conflict_marker(START_MARKER, id, add_del)
38 }
39 fn begin_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
40 self.output_conflict_marker::<C>(START_MARKER, id, None)
41 }
42 fn conflict_next<C: ChangeStore>(
43 &mut self,
44 id: usize,
45 side: Option<(&C, &[&Hash])>,
46 ) -> Result<(), std::io::Error> {
47 self.output_conflict_marker(SEPARATOR, id, side)
48 }
49 fn end_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
50 self.output_conflict_marker::<C>(END_MARKER, id, None)
51 }
52 fn end_zombie_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
53 self.end_conflict::<C>(id)
54 }
55 fn end_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
56 self.output_conflict_marker::<C>(END_MARKER, id, None)
57 }
58}
59
60pub(crate) struct ConflictsWriter<'a, 'b, W: std::io::Write> {
61 pub w: W,
62 pub lines: usize,
63 pub new_line: bool,
64 pub path: &'b str,
65 pub inode_vertex: Position<ChangeId>,
66 pub conflicts: &'a mut Vec<crate::output::Conflict>,
67 pub buf: Vec<u8>,
68}
69
70impl<'a, 'b, W: std::io::Write> ConflictsWriter<'a, 'b, W> {
71 pub fn new(
72 w: W,
73 path: &'b str,
74 inode_vertex: Position<ChangeId>,
75 conflicts: &'a mut Vec<crate::output::Conflict>,
76 ) -> Self {
77 ConflictsWriter {
78 inode_vertex,
79 w,
80 new_line: true,
81 lines: 1,
82 path,
83 conflicts,
84 buf: Vec::new(),
85 }
86 }
87}
88
89impl<'a, 'b, W: std::io::Write> std::ops::Deref for ConflictsWriter<'a, 'b, W> {
90 type Target = W;
91 fn deref(&self) -> &Self::Target {
92 &self.w
93 }
94}
95
96impl<'a, 'b, W: std::io::Write> std::ops::DerefMut for ConflictsWriter<'a, 'b, W> {
97 fn deref_mut(&mut self) -> &mut Self::Target {
98 &mut self.w
99 }
100}
101
102impl<'a, 'b, W: std::io::Write> VertexBuffer for ConflictsWriter<'a, 'b, W> {
103 fn output_line<E, C>(&mut self, v: Vertex<ChangeId>, c: C) -> Result<(), E>
104 where
105 E: From<std::io::Error>,
106 C: FnOnce(&mut [u8]) -> Result<(), E>,
107 {
108 self.buf.resize(v.end - v.start, 0);
109 c(&mut self.buf)?;
110 debug!("vbuf {:?} {:?}", v, std::str::from_utf8(&self.buf));
111 let ends_with_newline = self.buf.ends_with(b"\n");
112 self.lines += self.buf.iter().filter(|c| **c == b'\n').count();
113 self.w.write_all(&self.buf)?;
114 if !self.buf.is_empty() {
115 self.new_line = ends_with_newline;
118 }
119 Ok(())
120 }
121
122 fn output_conflict_marker<C: ChangeStore>(
123 &mut self,
124 s: &str,
125 id: usize,
126 sides: Option<(&C, &[&Hash])>,
127 ) -> Result<(), std::io::Error> {
128 debug!("output_conflict_marker {:?}", self.new_line);
129 if !self.new_line {
130 self.lines += 2;
131 self.w.write_all(b"\n")?;
132 } else {
133 self.lines += 1;
134 debug!("{:?}", s.as_bytes());
135 }
136 write!(self.w, "{} {}", s, id)?;
137 if let Some((changes, sides)) = sides {
138 for side in sides {
139 let h = side.to_base32();
140 write!(
141 self.w,
142 " [{} {}]",
143 h.split_at(8).0,
144 change_message(changes, side)
145 )?;
146 }
147 };
148 self.w.write_all(b"\n")?;
149 self.new_line = true;
150 Ok(())
151 }
152
153 fn begin_conflict<C: ChangeStore>(
154 &mut self,
155 id: usize,
156 sides: Option<(&C, &[&Hash])>,
157 ) -> Result<(), std::io::Error> {
158 self.conflicts.push(crate::output::Conflict::Order {
159 path: self.path.to_string(),
160 inode_vertex: [self.inode_vertex],
161 line: self.lines,
162 changes: sides
163 .iter()
164 .flat_map(|(_, b)| b.iter())
165 .cloned()
166 .cloned()
167 .collect(),
168 id,
169 });
170 self.output_conflict_marker(START_MARKER, id, sides)
171 }
172 fn begin_zombie_conflict<C: ChangeStore>(
173 &mut self,
174 id: usize,
175 add_del: Option<(&C, &[&Hash])>,
176 ) -> Result<(), std::io::Error> {
177 self.conflicts.push(crate::output::Conflict::Zombie {
178 path: self.path.to_string(),
179 inode_vertex: [self.inode_vertex],
180 line: self.lines,
181 changes: add_del
182 .iter()
183 .flat_map(|(_, b)| b.iter())
184 .cloned()
185 .cloned()
186 .collect(),
187 id,
188 });
189 self.output_conflict_marker(START_MARKER, id, add_del)
190 }
191 fn begin_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
192 self.conflicts.push(crate::output::Conflict::Cyclic {
193 path: self.path.to_string(),
194 inode_vertex: [self.inode_vertex],
195 line: self.lines,
196 changes: Vec::new(),
197 id,
198 });
199 self.output_conflict_marker::<C>(START_MARKER, id, None)
200 }
201 fn conflict_next<C: ChangeStore>(
202 &mut self,
203 id_: usize,
204 sides: Option<(&C, &[&Hash])>,
205 ) -> Result<(), std::io::Error> {
206 for conflict in self.conflicts.iter_mut().rev() {
207 match conflict {
208 crate::output::Conflict::Order { id, changes, .. } if *id == id_ => {
209 changes.extend(sides.into_iter().flat_map(|(_, b)| b.iter()).cloned())
210 }
211 crate::output::Conflict::Zombie { id, changes, .. } if *id == id_ => {
212 changes.extend(sides.into_iter().flat_map(|(_, b)| b.iter()).cloned())
213 }
214 crate::output::Conflict::Cyclic { id, changes, .. } if *id == id_ => {
215 changes.extend(sides.into_iter().flat_map(|(_, b)| b.iter()).cloned())
216 }
217 _ => break,
218 }
219 }
220 self.output_conflict_marker(SEPARATOR, id_, sides)
221 }
222}
223
224pub fn change_message<C: ChangeStore>(changes: &C, hash: &Hash) -> String {
225 match changes.get_header(hash) {
226 Ok(header) => {
227 if let Some(l) = header.message.lines().next() {
228 l.to_string()
229 } else {
230 String::new()
231 }
232 }
233 Err(_e) => "".to_string(),
234 }
235}
236
237pub struct Writer<W: std::io::Write> {
238 w: W,
239 buf: Vec<u8>,
240 new_line: bool,
241 is_zombie: bool,
242}
243
244impl<W: std::io::Write> Writer<W> {
245 pub fn new(w: W) -> Self {
246 Writer {
247 w,
248 new_line: true,
249 buf: Vec::new(),
250 is_zombie: false,
251 }
252 }
253 pub fn into_inner(self) -> W {
254 self.w
255 }
256}
257
258impl<W: std::io::Write> std::ops::Deref for Writer<W> {
259 type Target = W;
260 fn deref(&self) -> &Self::Target {
261 &self.w
262 }
263}
264
265impl<W: std::io::Write> std::ops::DerefMut for Writer<W> {
266 fn deref_mut(&mut self) -> &mut Self::Target {
267 &mut self.w
268 }
269}
270
271impl<W: std::io::Write> VertexBuffer for Writer<W> {
272 fn output_line<E, C>(&mut self, v: Vertex<ChangeId>, c: C) -> Result<(), E>
273 where
274 E: From<std::io::Error>,
275 C: FnOnce(&mut [u8]) -> Result<(), E>,
276 {
277 self.buf.resize(v.end - v.start, 0);
278 c(&mut self.buf)?;
279 debug!("vbuf {:?} {:?}", v, std::str::from_utf8(&self.buf));
280 let ends_with_newline = self.buf.ends_with(b"\n");
281 self.w.write_all(&self.buf[..])?;
282 if !self.buf.is_empty() {
283 self.new_line = ends_with_newline;
286 }
287 Ok(())
288 }
289
290 fn output_conflict_marker<C: ChangeStore>(
291 &mut self,
292 s: &str,
293 id: usize,
294 sides: Option<(&C, &[&Hash])>,
295 ) -> Result<(), std::io::Error> {
296 debug!("output_conflict_marker {:?}", self.new_line);
297 if !self.new_line {
298 self.w.write_all(b"\n")?;
299 }
300 write!(self.w, "{} {}", s, id)?;
301 if let Some((changes, sides)) = sides {
302 for side in sides {
303 let h = side.to_base32();
304 write!(
305 self.w,
306 " [{} {}]",
307 h.split_at(8).0,
308 change_message(changes, side)
309 )?;
310 }
311 };
312 self.w.write_all(b"\n")?;
313 Ok(())
314 }
315
316 fn begin_conflict<C: ChangeStore>(
317 &mut self,
318 id: usize,
319 side: Option<(&C, &[&Hash])>,
320 ) -> Result<(), std::io::Error> {
321 self.output_conflict_marker(START_MARKER, id, side)
322 }
323 fn end_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
324 self.is_zombie = false;
325 self.output_conflict_marker::<C>(END_MARKER, id, None)
326 }
327 fn begin_zombie_conflict<C: ChangeStore>(
328 &mut self,
329 id: usize,
330 add_del: Option<(&C, &[&Hash])>,
331 ) -> Result<(), std::io::Error> {
332 if self.is_zombie {
333 Ok(())
334 } else {
335 self.is_zombie = true;
336 self.output_conflict_marker(START_MARKER, id, add_del)
337 }
338 }
339 fn end_zombie_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
340 self.is_zombie = false;
341 self.output_conflict_marker::<C>(END_MARKER, id, None)
342 }
343 fn begin_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
344 self.output_conflict_marker::<C>(START_MARKER, id, None)
345 }
346}