1use crate::{DrivenConfig, Result, sync::SyncEngine};
4use std::path::Path;
5
6#[derive(Debug)]
8pub struct SyncCommand;
9
10impl SyncCommand {
11 pub fn run(project_root: &Path) -> Result<()> {
13 let config = Self::load_config(project_root)?;
14 let engine = SyncEngine::new(&config.sync.source_of_truth, config.editors);
15
16 let spinner = super::create_spinner("Syncing rules to all editors...");
17 let report = engine.sync(project_root)?;
18 spinner.finish_and_clear();
19
20 if report.is_success() {
22 super::print_success(&format!(
23 "Synced rules to {} editors",
24 report.synced_count()
25 ));
26 for synced in &report.synced {
27 println!(" {} → {}", synced.editor, synced.path.display());
28 }
29 } else {
30 super::print_warning(&format!(
31 "Synced {} editors, {} errors",
32 report.synced_count(),
33 report.errors.len()
34 ));
35 for error in &report.errors {
36 super::print_error(&format!(" {} - {}", error.editor, error.error));
37 }
38 }
39
40 Ok(())
41 }
42
43 pub fn watch(project_root: &Path) -> Result<()> {
45 let config = Self::load_config(project_root)?;
46 let mut engine = SyncEngine::new(&config.sync.source_of_truth, config.editors.clone());
47
48 super::print_info("Starting file watcher...");
49 engine.start_watching(project_root)?;
50
51 super::print_success("Watching for changes. Press Ctrl+C to stop.");
52 println!();
53
54 Self::run(project_root)?;
56
57 loop {
60 std::thread::sleep(std::time::Duration::from_secs(1));
61 }
63 }
64
65 fn load_config(project_root: &Path) -> Result<DrivenConfig> {
66 let config_path = project_root.join(".driven/config.toml");
67
68 if config_path.exists() {
69 let content = std::fs::read_to_string(&config_path)
70 .map_err(|e| crate::DrivenError::Config(format!("Failed to read config: {}", e)))?;
71 toml::from_str(&content)
72 .map_err(|e| crate::DrivenError::Config(format!("Failed to parse config: {}", e)))
73 } else {
74 Ok(DrivenConfig::default())
75 }
76 }
77}