clnrm_core/cli/commands/
dev.rs1use crate::cli::types::CliConfig;
13use crate::error::{CleanroomError, Result};
14use crate::watch::WatchConfig;
15use std::path::PathBuf;
16use tracing::{info, warn};
17
18pub use crate::watch::WatchConfig as DevConfig;
20
21pub struct DevWatcher;
23
24pub async fn run_dev_mode_with_filters(
59 paths: Option<Vec<PathBuf>>,
60 debounce_ms: u64,
61 clear_screen: bool,
62 only_pattern: Option<String>,
63 timebox_ms: Option<u64>,
64 cli_config: CliConfig,
65) -> Result<()> {
66 info!("🚀 Starting development mode with file watching");
67
68 if let Some(ref pattern) = only_pattern {
70 info!("🔍 Filtering scenarios matching pattern: {}", pattern);
71 }
72 if let Some(timeout) = timebox_ms {
73 info!("⏱️ Timeboxing scenarios to {}ms", timeout);
74 }
75
76 let watch_paths = match paths {
78 Some(paths) if !paths.is_empty() => paths,
79 _ => {
80 info!("No paths specified, watching current directory");
82 vec![PathBuf::from(".")]
83 }
84 };
85
86 for path in &watch_paths {
88 if !path.exists() {
89 return Err(CleanroomError::validation_error(format!(
90 "Path does not exist: {}",
91 path.display()
92 ))
93 .with_context("Cannot watch non-existent path"));
94 }
95
96 if !path.is_dir() && !path.is_file() {
97 return Err(CleanroomError::validation_error(format!(
98 "Path is not a file or directory: {}",
99 path.display()
100 ))
101 .with_context("Watch path must be a file or directory"));
102 }
103 }
104
105 info!("Watch configuration:");
107 info!(" Paths: {:?}", watch_paths);
108 info!(" Debounce: {}ms", debounce_ms);
109 info!(" Clear screen: {}", clear_screen);
110 if let Some(ref pattern) = only_pattern {
111 info!(" Filter pattern: {}", pattern);
112 }
113 if let Some(timeout) = timebox_ms {
114 info!(" Timebox: {}ms", timeout);
115 }
116 info!(" Parallel: {}", cli_config.parallel);
117 info!(" Jobs: {}", cli_config.jobs);
118
119 if debounce_ms < 50 {
121 warn!(
122 "⚠️ Debounce delay is very low ({}ms), may cause excessive runs",
123 debounce_ms
124 );
125 } else if debounce_ms > 2000 {
126 warn!(
127 "⚠️ Debounce delay is very high ({}ms), may feel sluggish",
128 debounce_ms
129 );
130 }
131
132 let mut watch_config =
134 WatchConfig::new(watch_paths, debounce_ms, clear_screen).with_cli_config(cli_config);
135
136 if let Some(pattern) = only_pattern {
138 watch_config = watch_config.with_filter_pattern(pattern);
139 }
140 if let Some(timeout) = timebox_ms {
141 watch_config = watch_config.with_timebox(timeout);
142 }
143
144 info!("📁 Watching for .toml.tera file changes...");
146 if watch_config.has_filter_pattern() {
147 info!("🔍 Filtering scenarios by pattern");
148 }
149 if watch_config.has_timebox() {
150 info!("⏱️ Timeboxing enabled");
151 }
152 info!("Press Ctrl+C to stop");
153
154 crate::watch::watch_and_run(watch_config).await?;
156
157 Ok(())
158}
159
160pub async fn run_dev_mode(
164 paths: Option<Vec<PathBuf>>,
165 debounce_ms: u64,
166 clear_screen: bool,
167 cli_config: CliConfig,
168) -> Result<()> {
169 run_dev_mode_with_filters(paths, debounce_ms, clear_screen, None, None, cli_config).await
170}