Skip to main content

cc_audit/run/
watch.rs

1//! Watch mode support for continuous scanning.
2
3use crate::{Cli, FileWatcher};
4
5use super::client::resolve_scan_paths;
6use super::formatter::format_result;
7use super::scanner::run_scan;
8
9/// Result of running in watch mode.
10#[derive(Debug)]
11pub enum WatchModeResult {
12    /// Watcher was successfully set up, initial scan was done.
13    Success,
14    /// Failed to create watcher.
15    WatcherCreationFailed(String),
16    /// Failed to watch a path.
17    WatchPathFailed(String, String),
18}
19
20/// Set up watch mode and return the file watcher.
21pub fn setup_watch_mode(cli: &Cli) -> Result<FileWatcher, WatchModeResult> {
22    let mut watcher = match FileWatcher::new() {
23        Ok(w) => w,
24        Err(e) => {
25            return Err(WatchModeResult::WatcherCreationFailed(e.to_string()));
26        }
27    };
28
29    // Resolve and watch all paths
30    let watch_paths = resolve_scan_paths(cli);
31    for path in &watch_paths {
32        if let Err(e) = watcher.watch(path) {
33            return Err(WatchModeResult::WatchPathFailed(
34                path.display().to_string(),
35                e.to_string(),
36            ));
37        }
38    }
39
40    Ok(watcher)
41}
42
43/// Run one iteration of the watch loop.
44pub fn watch_iteration(cli: &Cli) -> Option<String> {
45    run_scan(cli).map(|result| format_result(cli, &result))
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use std::path::PathBuf;
52    use tempfile::TempDir;
53
54    #[test]
55    fn test_watch_mode_result_variants() {
56        let success = WatchModeResult::Success;
57        assert!(matches!(success, WatchModeResult::Success));
58
59        let failed = WatchModeResult::WatcherCreationFailed("error".to_string());
60        assert!(matches!(failed, WatchModeResult::WatcherCreationFailed(_)));
61
62        let path_failed =
63            WatchModeResult::WatchPathFailed("/path".to_string(), "error".to_string());
64        assert!(matches!(
65            path_failed,
66            WatchModeResult::WatchPathFailed(_, _)
67        ));
68    }
69
70    #[test]
71    fn test_setup_watch_mode_valid_path() {
72        let temp_dir = TempDir::new().unwrap();
73        let cli = Cli {
74            paths: vec![temp_dir.path().to_path_buf()],
75            ..Default::default()
76        };
77
78        let result = setup_watch_mode(&cli);
79        assert!(result.is_ok());
80    }
81
82    #[test]
83    fn test_setup_watch_mode_invalid_path() {
84        let cli = Cli {
85            paths: vec![PathBuf::from("/nonexistent/path/12345")],
86            ..Default::default()
87        };
88
89        let result = setup_watch_mode(&cli);
90        assert!(result.is_err());
91    }
92}