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}