gix_diff/blob/unified_diff/mod.rs
1//! Facilities to produce the unified diff format.
2//!
3//! Originally based on <https://github.com/pascalkuthe/imara-diff/pull/14>.
4
5/// Defines the size of the context printed before and after each change.
6///
7/// Similar to the `-U` option in git diff or gnu-diff. If the context overlaps
8/// with previous or next change, the context gets reduced accordingly.
9#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
10pub struct ContextSize {
11 /// Defines the size of the context printed before and after each change.
12 symmetrical: u32,
13}
14
15impl Default for ContextSize {
16 fn default() -> Self {
17 ContextSize::symmetrical(3)
18 }
19}
20
21/// Instantiation
22impl ContextSize {
23 /// Create a symmetrical context with `n` lines before and after a changed hunk.
24 pub fn symmetrical(n: u32) -> Self {
25 ContextSize { symmetrical: n }
26 }
27}
28
29/// Represents the type of a line in a unified diff.
30#[doc(alias = "git2")]
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
32pub enum DiffLineKind {
33 /// A line that exists in both the old and the new version, added based on [`ContextSize`].
34 Context,
35 /// A line that was added in the new version.
36 Add,
37 /// A line that was removed from the old version.
38 Remove,
39}
40
41/// Holds information about a unified diff hunk, specifically with respect to line numbers.
42#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
43pub struct HunkHeader {
44 /// The 1-based start position in the 'before' lines.
45 pub before_hunk_start: u32,
46 /// The size of the 'before' hunk in lines.
47 pub before_hunk_len: u32,
48 /// The 1-based start position in the 'after' lines.
49 pub after_hunk_start: u32,
50 /// The size of the 'after' hunk in lines.
51 pub after_hunk_len: u32,
52}
53
54/// An adapter with [`ConsumeHunk`] implementation to call a delegate which receives each stringified hunk.
55pub struct ConsumeBinaryHunk<'a, D> {
56 /// The newline to use to separate lines if these don't yet contain a newline.
57 /// It should also be used to separate the stringified header from the hunk itself.
58 pub newline: &'a str,
59 /// The delegate to receive stringified hunks.
60 pub delegate: D,
61
62 header_buf: String,
63 hunk_buf: Vec<u8>,
64}
65
66/// A trait for use in conjunction with [`ConsumeBinaryHunk`].
67pub trait ConsumeBinaryHunkDelegate {
68 /// Consume a single `hunk` in unified diff format, along with its `header_str` that already has a trailing newline added based
69 /// on the parent [`ConsumeBinaryHunk`] configuration, also in unified diff format.
70 /// The `header` is the data used to produce `header_str`.
71 fn consume_binary_hunk(&mut self, header: HunkHeader, header_str: &str, hunk: &[u8]) -> std::io::Result<()>;
72}
73
74/// A utility trait for use in [`UnifiedDiff`](super::UnifiedDiff).
75pub trait ConsumeHunk {
76 /// The item this instance produces after consuming all hunks.
77 type Out;
78
79 /// Consume a single hunk which is represented by its `lines`, each of which with a `DiffLineKind` value
80 /// to know if it's added, removed or context.
81 /// The `header` specifies hunk offsets, which positions the `lines` in the old and new file respectively.
82 ///
83 /// Note that the [`UnifiedDiff`](super::UnifiedDiff) sink will wrap its output in an [`std::io::Result`].
84 /// After this method returned its first error, it will not be called anymore.
85 fn consume_hunk(&mut self, header: HunkHeader, lines: &[(DiffLineKind, &[u8])]) -> std::io::Result<()>;
86
87 /// Called after the last hunk is consumed to produce an output.
88 fn finish(self) -> Self::Out;
89}
90
91pub(super) mod impls;