use std::ops::Range; /// Trait for processing the edit-scripts computed with [`diff`](crate::diff) pub trait Sink: Sized { type Out; /// This method is called whenever a diff [`algorithm`](crate::Algorithm) /// finds a change between the two processed input file. /// A change is a continous subsequence of [tokens](crate::intern::Token) `before` that needs /// to be replaced by a different contious subsequence of tokens `after` to construct the seconds file from the first. /// /// These token subsequences are passed to this function in in ** strictly montonically increasing order**. /// That means that for two subsequenct calls `process_change(before1, after1)` and `process_change(before2, after2)` /// the following always holds: /// /// ``` no_compile /// assert!(before1.end < before2.start); /// assert!(after1.end < after2.start); /// ``` /// /// # Paramters /// - **`before`** - the **position** of the removed token subsequence in the orignal file. /// - **`after`** - the **position** of the inserted token subsequence in the destination file. /// /// # Notes //// /// A `Sink` has no function to indicate that a section of a file remains unchanged. /// However due to the montonically increasing calls, implementations can easily determine /// which subsequences remain unchanged by saving `before.end`/`after.end`. /// The range between `before.start`/`after.end` and the previous `before.end`/`after.end` /// is always unchanged. fn process_change(&mut self, before: Range, after: Range); /// This function is called after all calls to `process_change` are complete /// to obtain the final diff result fn finish(self) -> Self::Out; /// Utility method that constructs a [`Counter`](crate::sink::Counter) that tracks the total number /// of inserted and removed tokens in the changes passed to [`process_change`](crate::Sink::process_change). fn with_counter(self) -> Counter { Counter::new(self) } } impl, Range)> Sink for T { type Out = (); fn process_change(&mut self, before: Range, after: Range) { self(before, after) } fn finish(self) -> Self::Out {} } impl Sink for () { type Out = (); fn process_change(&mut self, _before: Range, _after: Range) {} fn finish(self) -> Self::Out {} } /// A [`Sink`](crate::Sink) which wraps a different sink /// and counts the number of `removed` and `inserted` [tokens](crate::intern::Token). pub struct Counter { /// Total number of recorded inserted [`tokens`](crate::intern::Token). /// Computed by summing the lengths of the `after` subsequences pass to [`process_change`](crate::Sink::process_change). pub removals: u32, /// Total number of recorded inserted [`tokens`](crate::intern::Token). /// Computed by summing the lengths of the `after` subsequences pass to [`process_change`](crate::Sink::process_change). pub insertions: u32, /// The [`Sink`](crate::Sink) for which the counter records [`tokens`](crate::intern::Token). /// All calls to [`process_change`](crate::Sink::process_change) are forwarded to the `sink` by the counter. /// After [`finish`](crate::Sink::finish) is called, this field contains the output returned by the [`finish`](crate::Sink::finish) /// method of the wrapped [`Sink`](crate::Sink) pub wrapped: T, } impl Counter { pub fn new(sink: S) -> Self { Self { insertions: 0, removals: 0, wrapped: sink } } } impl Sink for Counter { type Out = Counter; fn process_change(&mut self, before: Range, after: Range) { self.removals += before.end - before.start; self.insertions += after.end - after.start; self.wrapped.process_change(before, after) } fn finish(self) -> Self::Out { Counter { removals: self.removals, insertions: self.insertions, wrapped: self.wrapped.finish(), } } } impl Counter { pub fn total(&self) -> usize { self.insertions as usize + self.removals as usize } } impl Default for Counter<()> { fn default() -> Self { Counter::new(()) } }