imara_diff/
sink.rs

1use std::ops::Range;
2
3/// Trait for processing the edit-scripts computed with [`diff`](crate::diff)
4pub trait Sink: Sized {
5    type Out;
6
7    /// This method is called whenever a diff [`algorithm`](crate::Algorithm)
8    /// finds a change between the two processed input files.
9    /// A change is a continuous subsequence of [tokens](crate::intern::Token) `before` that needs
10    /// to be replaced by a different continuous subsequence of tokens `after` to construct the second file from the first.
11    ///
12    /// These token subsequences are passed to this function in in ** strictly monotonically increasing order**.
13    /// That means that for two subsequent calls `process_change(before1, after1)` and `process_change(before2, after2)`
14    /// the following always holds:
15    ///
16    /// ``` no_compile
17    /// assert!(before1.end < before2.start);
18    /// assert!(after1.end < after2.start);
19    /// ```
20    ///
21    /// # Parameters
22    /// - **`before`** - the **position** of the removed token subsequence in the orignal file.
23    /// - **`after`** - the **position** of the inserted token subsequence in the destination file.
24    ///
25    /// # Notes
26    ////
27    /// A `Sink` has no function to indicate that a section of a file remains unchanged.
28    /// However due to the monotonically increasing calls, implementations can easily determine
29    /// which subsequences remain unchanged by saving `before.end`/`after.end`.
30    /// The range between `before.start`/`after.end` and the previous `before.end`/`after.end`
31    /// is always unchanged.
32    fn process_change(&mut self, before: Range<u32>, after: Range<u32>);
33
34    /// This function is called after all calls to `process_change` are complete
35    /// to obtain the final diff result
36    fn finish(self) -> Self::Out;
37
38    /// Utility method that constructs a [`Counter`] that tracks the total number
39    /// of inserted and removed tokens in the changes passed to [`process_change`](crate::Sink::process_change).
40    fn with_counter(self) -> Counter<Self> {
41        Counter::new(self)
42    }
43}
44
45impl<T: FnMut(Range<u32>, Range<u32>)> Sink for T {
46    type Out = ();
47
48    fn process_change(&mut self, before: Range<u32>, after: Range<u32>) {
49        self(before, after)
50    }
51
52    fn finish(self) -> Self::Out {}
53}
54
55impl Sink for () {
56    type Out = ();
57    fn process_change(&mut self, _before: Range<u32>, _after: Range<u32>) {}
58    fn finish(self) -> Self::Out {}
59}
60
61/// A [`Sink`] which wraps a different sink
62/// and counts the number of `removed` and `inserted` [tokens](crate::intern::Token).
63pub struct Counter<T> {
64    /// Total number of recorded inserted [`tokens`](crate::intern::Token).
65    /// Computed by summing the lengths of the `after` subsequences pass to [`process_change`](crate::Sink::process_change).
66    pub removals: u32,
67    /// Total number of recorded inserted [`tokens`](crate::intern::Token).
68    /// Computed by summing the lengths of the `after` subsequences pass to [`process_change`](crate::Sink::process_change).
69    pub insertions: u32,
70    /// The [`Sink`] for which the counter records [`tokens`](crate::intern::Token).
71    /// All calls to [`process_change`](crate::Sink::process_change) are forwarded to the `sink` by the counter.
72    /// After [`finish`](crate::Sink::finish) is called, this field contains the output returned by the [`finish`](crate::Sink::finish)
73    /// method of the wrapped [`Sink`].
74    pub wrapped: T,
75}
76
77impl<S: Sink> Counter<S> {
78    pub fn new(sink: S) -> Self {
79        Self {
80            insertions: 0,
81            removals: 0,
82            wrapped: sink,
83        }
84    }
85}
86
87impl<S: Sink> Sink for Counter<S> {
88    type Out = Counter<S::Out>;
89    fn process_change(&mut self, before: Range<u32>, after: Range<u32>) {
90        self.removals += before.end - before.start;
91        self.insertions += after.end - after.start;
92        self.wrapped.process_change(before, after)
93    }
94
95    fn finish(self) -> Self::Out {
96        Counter {
97            removals: self.removals,
98            insertions: self.insertions,
99            wrapped: self.wrapped.finish(),
100        }
101    }
102}
103
104impl<T> Counter<T> {
105    pub fn total(&self) -> usize {
106        self.insertions as usize + self.removals as usize
107    }
108}
109
110impl Default for Counter<()> {
111    fn default() -> Self {
112        Counter::new(())
113    }
114}