Skip to main content

aaai_core/diff/
progress.rs

1//! Progress reporting for the diff engine.
2//!
3//! The diff engine emits [`DiffProgress`] events through any type that
4//! implements [`ProgressSink`].  The CLI uses this to drive an `indicatif`
5//! progress bar; the GUI can route events to an iced channel.
6
7/// A single progress event emitted during a diff walk.
8#[derive(Debug, Clone)]
9pub enum DiffProgress {
10    /// Directory walk started; `total` is the approximate number of unique paths.
11    Started { total: usize },
12    /// One path has been processed.
13    File { path: String, processed: usize, total: usize },
14    /// All files have been processed; engine is sorting.
15    Sorting,
16    /// The diff is complete.
17    Done { total_files: usize },
18}
19
20/// Anything that can receive progress events.
21pub trait ProgressSink: Send + Sync {
22    fn emit(&self, event: DiffProgress);
23}
24
25/// A sink that discards all events (default / no-op).
26#[derive(Default)]
27pub struct NullProgress;
28
29impl ProgressSink for NullProgress {
30    fn emit(&self, _: DiffProgress) {}
31}
32
33/// A sink backed by a `std::sync::mpsc` channel.
34pub struct ChannelProgress {
35    tx: std::sync::mpsc::Sender<DiffProgress>,
36}
37
38impl ChannelProgress {
39    pub fn new(tx: std::sync::mpsc::Sender<DiffProgress>) -> Self {
40        Self { tx }
41    }
42}
43
44impl ProgressSink for ChannelProgress {
45    fn emit(&self, event: DiffProgress) {
46        let _ = self.tx.send(event);
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use std::sync::mpsc;
54
55    #[test]
56    fn channel_progress_sends_events() {
57        let (tx, rx) = mpsc::channel();
58        let sink = ChannelProgress::new(tx);
59        sink.emit(DiffProgress::Started { total: 10 });
60        sink.emit(DiffProgress::Done { total_files: 10 });
61        let events: Vec<_> = rx.try_iter().collect();
62        assert_eq!(events.len(), 2);
63    }
64
65    #[test]
66    fn null_progress_does_not_panic() {
67        let sink = NullProgress;
68        sink.emit(DiffProgress::Started { total: 0 });
69        sink.emit(DiffProgress::Sorting);
70    }
71}