tcss_cli/commands/
watch.rs1use 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
13pub 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 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 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 output::verbose("Watching for changes...");
42 loop {
43 match rx.recv() {
44 Ok(Ok(Event { .. })) => {
45 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
62pub 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_directory(path, output, minify, verbose)?;
79
80 let (tx, rx) = channel();
82 let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
83
84 watcher.watch(path, RecursiveMode::Recursive)?;
85
86 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 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
116fn 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
143fn 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