Skip to main content

common/
dry_run.rs

1//! Dry-run reporting helpers shared by copy, link, and rm operations.
2
3use crate::config::DryRunMode;
4use crate::filter::{FilterResult, TimeSkipReason};
5
6/// Reports a dry-run action (would copy/link/remove) to stdout.
7/// When `dst` is `None`, prints only the source path (used by rm).
8pub fn report_action(
9    verb: &str,
10    src: &std::path::Path,
11    dst: Option<&std::path::Path>,
12    entry_type: &str,
13) {
14    match dst {
15        Some(dst) => println!("would {} {} {:?} -> {:?}", verb, entry_type, src, dst),
16        None => println!("would {} {} {:?}", verb, entry_type, src),
17    }
18}
19
20/// Reports a skipped entry during dry-run to stdout.
21/// Respects dry-run mode: Brief suppresses, All shows path, Explain shows reason.
22pub fn report_skip(
23    path: &std::path::Path,
24    result: &FilterResult,
25    mode: DryRunMode,
26    entry_type: &str,
27) {
28    match mode {
29        DryRunMode::Brief => { /* brief mode doesn't show skipped files */ }
30        DryRunMode::All => {
31            println!("skip {} {:?}", entry_type, path);
32        }
33        DryRunMode::Explain => match result {
34            FilterResult::ExcludedByDefault => {
35                println!(
36                    "skip {} {:?} (no include pattern matched)",
37                    entry_type, path
38                );
39            }
40            FilterResult::ExcludedByPattern(pattern) => {
41                println!("skip {} {:?} (excluded by '{}')", entry_type, path, pattern);
42            }
43            FilterResult::Included => { /* shouldn't happen */ }
44        },
45    }
46}
47
48/// Returns a human-readable skip reason for a FilterResult.
49/// Returns None for Included (which is not a skip).
50/// Used by the remote dry-run path to format structured log messages.
51pub fn format_skip_reason(result: &FilterResult) -> Option<String> {
52    match result {
53        FilterResult::Included => None,
54        FilterResult::ExcludedByDefault => Some("no include pattern matched".to_string()),
55        FilterResult::ExcludedByPattern(p) => Some(format!("excluded by '{}'", p)),
56    }
57}
58
59/// Reports an entry skipped by a time filter during dry-run to stdout.
60/// Respects dry-run mode: Brief suppresses, All shows path, Explain shows reason.
61pub fn report_time_skip(
62    path: &std::path::Path,
63    reason: TimeSkipReason,
64    mode: DryRunMode,
65    entry_type: &str,
66) {
67    match mode {
68        DryRunMode::Brief => { /* brief mode doesn't show skipped entries */ }
69        DryRunMode::All => {
70            println!("skip {} {:?}", entry_type, path);
71        }
72        DryRunMode::Explain => {
73            let reason_str = match reason {
74                TimeSkipReason::TooNewModified => "mtime is too recent",
75                TimeSkipReason::TooNewCreated => "btime is too recent",
76                TimeSkipReason::TooNewBoth => "mtime and btime are too recent",
77            };
78            println!("skip {} {:?} ({})", entry_type, path, reason_str);
79        }
80    }
81}