Skip to main content

pijul_core/
vertex_buffer.rs

1use crate::{changestore::ChangeStore, pristine::*};
2
3pub const START_MARKER: &str = ">>>>>>>";
4
5pub const SEPARATOR: &str = "=======";
6
7pub const END_MARKER: &str = "<<<<<<<";
8
9/// A trait for outputting keys and their contents. This trait allows
10/// to retain more information about conflicts than directly
11/// outputting as bytes to a `Write`. The diff algorithm uses that
12/// information, for example.
13pub 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            // empty "lines" (such as in the beginning of a file)
116            // don't change the status of self.new_line.
117            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            // empty "lines" (such as in the beginning of a file)
284            // don't change the status of self.new_line.
285            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}