Skip to main content

ripsync_core/
report.rs

1//! Progress reporting plumbing shared by the plain CLI output and the TUI.
2//!
3//! The engine emits [`Event`]s through a [`Reporter`]; the CLI decides how to
4//! render them (line output, JSON, or a live `ratatui` dashboard). The trait is
5//! `Sync` so the parallel apply phase can report from worker threads.
6
7use std::path::PathBuf;
8
9use crate::plan::Action;
10
11/// Lifecycle phase of a sync run.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum RunPhase {
14    /// Walking and classifying trees.
15    Planning,
16    /// Awaiting destructive-operation approval.
17    Review,
18    /// Creating directories and copying entries.
19    Copying,
20    /// Removing destination-only entries.
21    Deleting,
22    /// Comparing source and destination.
23    Verifying,
24    /// Persisting the destination index.
25    Finalizing,
26    /// Run completed successfully.
27    Done,
28    /// Run was cancelled.
29    Cancelled,
30    /// Run failed.
31    Failed,
32}
33
34/// Final run outcome.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum RunStatus {
37    /// All requested phases completed.
38    Success,
39    /// The user cancelled cooperatively.
40    Cancelled,
41    /// An operation or verification failed.
42    Failed,
43}
44
45/// A progress event emitted while applying a plan.
46#[derive(Debug, Clone)]
47pub enum Event {
48    /// The lifecycle phase changed.
49    Phase(RunPhase),
50    /// Planning walk/classification progress.
51    PlanningProgress {
52        /// Entries observed so far.
53        entries: usize,
54    },
55    /// Concrete backend selection and rationale.
56    BackendSelected {
57        /// Backend name.
58        backend: &'static str,
59        /// Selection reason.
60        reason: &'static str,
61    },
62    /// Emitted once before work starts.
63    Planned {
64        /// Number of files that will be copied or updated.
65        total_files: usize,
66        /// Total bytes those files comprise.
67        total_bytes: u64,
68        /// Number of pending deletions.
69        deletions: usize,
70    },
71    /// A file transfer began.
72    FileStart {
73        /// Path relative to the destination root.
74        rel: PathBuf,
75        /// File length in bytes.
76        len: u64,
77    },
78    /// A file transfer finished.
79    FileDone {
80        /// Path relative to the destination root.
81        rel: PathBuf,
82        /// Whether it was a copy or an update.
83        action: Action,
84        /// Bytes written.
85        bytes: u64,
86    },
87    /// A directory was created or already present.
88    DirDone {
89        /// Path relative to the destination root.
90        rel: PathBuf,
91        /// The action taken.
92        action: Action,
93    },
94    /// A symlink was created or updated.
95    SymlinkDone {
96        /// Path relative to the destination root.
97        rel: PathBuf,
98        /// The action taken.
99        action: Action,
100    },
101    /// An entry was skipped (already up to date).
102    Skipped {
103        /// Path relative to the destination root.
104        rel: PathBuf,
105    },
106    /// An entry was deleted.
107    Deleted {
108        /// Path relative to the destination root.
109        rel: PathBuf,
110    },
111    /// An operation failed for a single entry (the sync continues).
112    Failed {
113        /// Path relative to the destination root.
114        rel: PathBuf,
115        /// Human-readable error text.
116        error: String,
117    },
118    /// Verification progress.
119    VerificationProgress {
120        /// Entries checked.
121        checked: usize,
122        /// Total entries scheduled.
123        total: usize,
124        /// Mismatches seen so far.
125        mismatches: usize,
126    },
127    /// A structured verification mismatch.
128    VerificationFailed {
129        /// Relative path.
130        rel: PathBuf,
131        /// What differed.
132        detail: String,
133    },
134    /// Final run outcome.
135    Finished {
136        /// Status of the run.
137        status: RunStatus,
138    },
139}
140
141/// Tally of everything a sync did.
142#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
143pub struct Stats {
144    /// Files/dirs/symlinks newly created.
145    pub copied: u64,
146    /// Entries overwritten.
147    pub updated: u64,
148    /// Entries left untouched.
149    pub skipped: u64,
150    /// Entries removed.
151    pub deleted: u64,
152    /// Per-entry failures.
153    pub errors: u64,
154    /// Bytes written for copies and updates.
155    pub bytes: u64,
156}
157
158/// A sink for [`Event`]s. Implementations must be cheap and thread-safe.
159pub trait Reporter: Sync {
160    /// Handle a single event.
161    fn event(&self, ev: Event);
162}
163
164/// A [`Reporter`] that ignores everything (useful for tests and benches).
165pub struct NullReporter;
166
167impl Reporter for NullReporter {
168    fn event(&self, _ev: Event) {}
169}