tcss_cli/commands/
watch.rs

1//! Watch command implementation
2
3use anyhow::{Context, Result};
4use colored::*;
5use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
6use std::path::{Path, PathBuf};
7use std::sync::mpsc::channel;
8use std::time::Duration;
9
10use super::compile::compile_file;
11use crate::output;
12
13/// Watch a single file for changes
14pub fn watch_file(
15    input: &Path,
16    output: Option<&Path>,
17    minify: bool,
18    verbose: bool,
19) -> Result<()> {
20    output::info(&format!("Watching: {}", input.display()));
21    output::verbose("Press Ctrl+C to stop");
22    output::debug_value("Watch mode", &"single file");
23    output::debug_value("File path", &input);
24    println!();
25
26    // Initial compilation
27    output::debug("Performing initial compilation...");
28    if let Err(e) = compile_file(input, output, minify, verbose) {
29        output::error(&e.to_string());
30    }
31
32    // Set up file watcher
33    output::debug("Setting up file watcher...");
34    let (tx, rx) = channel();
35    let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
36
37    watcher.watch(input, RecursiveMode::NonRecursive)?;
38    output::debug("File watcher initialized successfully");
39
40    // Watch loop
41    output::verbose("Watching for changes...");
42    loop {
43        match rx.recv() {
44            Ok(Ok(Event { .. })) => {
45                // Small delay to ensure file is fully written
46                std::thread::sleep(Duration::from_millis(100));
47
48                output::warning("File changed, recompiling...");
49                output::debug(&format!("Change detected in: {}", input.display()));
50
51                if let Err(e) = compile_file(input, output, minify, verbose) {
52                    output::error(&e.to_string());
53                }
54                println!();
55            }
56            Ok(Err(e)) => eprintln!("Watch error: {:?}", e),
57            Err(e) => eprintln!("Channel error: {:?}", e),
58        }
59    }
60}
61
62/// Watch a directory for TCSS file changes
63pub fn watch_directory(
64    path: &Path,
65    output: Option<&Path>,
66    minify: bool,
67    verbose: bool,
68) -> Result<()> {
69    println!(
70        "{} {}",
71        "Watching directory:".bright_blue().bold(),
72        path.display()
73    );
74    println!("{}", "Press Ctrl+C to stop".bright_black());
75    println!();
76
77    // Compile all TCSS files initially
78    compile_directory(path, output, minify, verbose)?;
79
80    // Set up file watcher
81    let (tx, rx) = channel();
82    let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
83
84    watcher.watch(path, RecursiveMode::Recursive)?;
85
86    // Watch loop
87    loop {
88        match rx.recv() {
89            Ok(Ok(event)) => {
90                if let Some(path) = event.paths.first() {
91                    if path.extension().and_then(|s| s.to_str()) == Some("tcss") {
92                        // Small delay to ensure file is fully written
93                        std::thread::sleep(Duration::from_millis(100));
94
95                        println!(
96                            "{} {}",
97                            "File changed:".bright_yellow(),
98                            path.display()
99                        );
100
101                        let output_path = determine_output_path(path, output);
102                        if let Err(e) = compile_file(path, output_path.as_deref(), minify, verbose)
103                        {
104                            eprintln!("{} {}", "Error:".bright_red().bold(), e);
105                        }
106                        println!();
107                    }
108                }
109            }
110            Ok(Err(e)) => eprintln!("Watch error: {:?}", e),
111            Err(e) => eprintln!("Channel error: {:?}", e),
112        }
113    }
114}
115
116/// Compile all TCSS files in a directory
117fn compile_directory(
118    path: &Path,
119    output: Option<&Path>,
120    minify: bool,
121    verbose: bool,
122) -> Result<()> {
123    let entries = std::fs::read_dir(path)
124        .with_context(|| format!("Failed to read directory: {}", path.display()))?;
125
126    for entry in entries {
127        let entry = entry?;
128        let path = entry.path();
129
130        if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("tcss") {
131            let output_path = determine_output_path(&path, output);
132            if let Err(e) = compile_file(&path, output_path.as_deref(), minify, verbose) {
133                eprintln!("{} {}", "Error:".bright_red().bold(), e);
134            }
135        } else if path.is_dir() {
136            compile_directory(&path, output, minify, verbose)?;
137        }
138    }
139
140    Ok(())
141}
142
143/// Determine the output path for a TCSS file
144fn determine_output_path(input: &Path, output_dir: Option<&Path>) -> Option<PathBuf> {
145    if let Some(dir) = output_dir {
146        let file_name = input.file_name()?;
147        let mut output_path = dir.join(file_name);
148        output_path.set_extension("css");
149        Some(output_path)
150    } else {
151        None
152    }
153}
154