sbom_tools/watch/
config.rs1use super::WatchError;
4use crate::config::{EnrichmentConfig, OutputConfig};
5use std::path::PathBuf;
6use std::time::Duration;
7
8#[derive(Debug, Clone)]
10pub struct WatchConfig {
11 pub watch_dirs: Vec<PathBuf>,
13 pub poll_interval: Duration,
15 pub enrich_interval: Duration,
17 pub debounce: Duration,
20 pub output: OutputConfig,
22 pub enrichment: EnrichmentConfig,
24 pub webhook_url: Option<String>,
26 pub exit_on_change: bool,
28 pub max_snapshots: usize,
30 pub quiet: bool,
32 pub dry_run: bool,
34 pub cra_standards_enabled: bool,
37 pub cra_standards_interval: Duration,
39 pub cra_standards_timeout: Duration,
41}
42
43pub fn parse_duration(s: &str) -> Result<Duration, WatchError> {
55 let s = s.trim();
56 if s.is_empty() {
57 return Err(WatchError::InvalidInterval(s.to_string()));
58 }
59
60 let (num_str, unit) = if let Some(stripped) = s.strip_suffix("ms") {
61 (stripped, "ms")
62 } else if s.ends_with('s') || s.ends_with('m') || s.ends_with('h') || s.ends_with('d') {
63 (&s[..s.len() - 1], &s[s.len() - 1..])
64 } else {
65 return Err(WatchError::InvalidInterval(s.to_string()));
66 };
67
68 let value: u64 = num_str
69 .parse()
70 .map_err(|_| WatchError::InvalidInterval(s.to_string()))?;
71
72 match unit {
73 "ms" => Ok(Duration::from_millis(value)),
74 "s" => Ok(Duration::from_secs(value)),
75 "m" => Ok(Duration::from_secs(value * 60)),
76 "h" => Ok(Duration::from_secs(value * 3600)),
77 "d" => Ok(Duration::from_secs(value * 86400)),
78 _ => Err(WatchError::InvalidInterval(s.to_string())),
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_parse_duration_seconds() {
88 assert_eq!(parse_duration("30s").unwrap(), Duration::from_secs(30));
89 }
90
91 #[test]
92 fn test_parse_duration_minutes() {
93 assert_eq!(parse_duration("5m").unwrap(), Duration::from_secs(300));
94 }
95
96 #[test]
97 fn test_parse_duration_hours() {
98 assert_eq!(parse_duration("1h").unwrap(), Duration::from_secs(3600));
99 }
100
101 #[test]
102 fn test_parse_duration_days() {
103 assert_eq!(parse_duration("2d").unwrap(), Duration::from_secs(172_800));
104 }
105
106 #[test]
107 fn test_parse_duration_milliseconds() {
108 assert_eq!(parse_duration("500ms").unwrap(), Duration::from_millis(500));
109 }
110
111 #[test]
112 fn test_parse_duration_with_whitespace() {
113 assert_eq!(parse_duration(" 10s ").unwrap(), Duration::from_secs(10));
114 }
115
116 #[test]
117 fn test_parse_duration_invalid_unit() {
118 assert!(parse_duration("10x").is_err());
119 }
120
121 #[test]
122 fn test_parse_duration_invalid_number() {
123 assert!(parse_duration("abcs").is_err());
124 }
125
126 #[test]
127 fn test_parse_duration_empty() {
128 assert!(parse_duration("").is_err());
129 }
130
131 #[test]
132 fn test_parse_duration_no_unit() {
133 assert!(parse_duration("100").is_err());
134 }
135}