goud_engine/assets/hot_reload/
config.rs1use std::collections::HashSet;
4use std::path::Path;
5use std::time::Duration;
6
7#[derive(Debug, Clone)]
13pub struct HotReloadConfig {
14 pub enabled: bool,
16
17 pub debounce_duration: Duration,
22
23 pub recursive: bool,
25
26 pub extensions: HashSet<String>,
28
29 pub ignore_hidden: bool,
31
32 pub ignore_temp: bool,
34}
35
36impl HotReloadConfig {
37 pub fn new() -> Self {
39 Self {
40 enabled: cfg!(debug_assertions), debounce_duration: Duration::from_millis(100),
42 recursive: true,
43 extensions: HashSet::new(), ignore_hidden: true,
45 ignore_temp: true,
46 }
47 }
48
49 pub fn with_enabled(mut self, enabled: bool) -> Self {
51 self.enabled = enabled;
52 self
53 }
54
55 pub fn with_debounce(mut self, duration: Duration) -> Self {
57 self.debounce_duration = duration;
58 self
59 }
60
61 pub fn with_recursive(mut self, recursive: bool) -> Self {
63 self.recursive = recursive;
64 self
65 }
66
67 pub fn watch_extension(mut self, ext: impl Into<String>) -> Self {
69 self.extensions.insert(ext.into());
70 self
71 }
72
73 pub fn with_ignore_hidden(mut self, ignore: bool) -> Self {
75 self.ignore_hidden = ignore;
76 self
77 }
78
79 pub fn with_ignore_temp(mut self, ignore: bool) -> Self {
81 self.ignore_temp = ignore;
82 self
83 }
84
85 pub fn should_watch(&self, path: &Path) -> bool {
87 if self.ignore_hidden {
89 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
90 if name.starts_with('.') {
91 return false;
92 }
93 }
94 }
95
96 if self.ignore_temp {
98 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
99 if name.ends_with('~')
100 || name.ends_with(".tmp")
101 || name.ends_with(".swp")
102 || name.ends_with(".bak")
103 {
104 return false;
105 }
106 }
107 }
108
109 if !self.extensions.is_empty() {
111 if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
112 if !self.extensions.contains(ext) {
113 return false;
114 }
115 } else {
116 return false;
118 }
119 }
120
121 true
122 }
123}
124
125impl Default for HotReloadConfig {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_default() {
141 let config = HotReloadConfig::default();
142 assert_eq!(config.enabled, cfg!(debug_assertions));
143 assert_eq!(config.debounce_duration, Duration::from_millis(100));
144 assert!(config.recursive);
145 assert!(config.ignore_hidden);
146 assert!(config.ignore_temp);
147 assert!(config.extensions.is_empty());
148 }
149
150 #[test]
151 fn test_with_enabled() {
152 let config = HotReloadConfig::new().with_enabled(false);
153 assert!(!config.enabled);
154 }
155
156 #[test]
157 fn test_with_debounce() {
158 let config = HotReloadConfig::new().with_debounce(Duration::from_millis(500));
159 assert_eq!(config.debounce_duration, Duration::from_millis(500));
160 }
161
162 #[test]
163 fn test_with_recursive() {
164 let config = HotReloadConfig::new().with_recursive(false);
165 assert!(!config.recursive);
166 }
167
168 #[test]
169 fn test_watch_extension() {
170 let config = HotReloadConfig::new()
171 .watch_extension("png")
172 .watch_extension("json");
173
174 assert!(config.extensions.contains("png"));
175 assert!(config.extensions.contains("json"));
176 assert_eq!(config.extensions.len(), 2);
177 }
178
179 #[test]
180 fn test_should_watch_all() {
181 let config = HotReloadConfig::new();
182
183 assert!(config.should_watch(Path::new("test.png")));
185 assert!(config.should_watch(Path::new("test.json")));
186 }
187
188 #[test]
189 fn test_should_watch_with_extension_filter() {
190 let config = HotReloadConfig::new().watch_extension("png");
191
192 assert!(config.should_watch(Path::new("test.png")));
193 assert!(!config.should_watch(Path::new("test.json")));
194 assert!(!config.should_watch(Path::new("no_extension")));
195 }
196
197 #[test]
198 fn test_should_watch_hidden_files() {
199 let config = HotReloadConfig::new().with_ignore_hidden(true);
200
201 assert!(config.should_watch(Path::new("test.png")));
202 assert!(!config.should_watch(Path::new(".hidden.png")));
203 }
204
205 #[test]
206 fn test_should_watch_temp_files() {
207 let config = HotReloadConfig::new().with_ignore_temp(true);
208
209 assert!(config.should_watch(Path::new("test.png")));
210 assert!(!config.should_watch(Path::new("test.png~")));
211 assert!(!config.should_watch(Path::new("test.tmp")));
212 assert!(!config.should_watch(Path::new("test.swp")));
213 assert!(!config.should_watch(Path::new("test.bak")));
214 }
215
216 #[test]
217 fn test_should_watch_allow_hidden() {
218 let config = HotReloadConfig::new().with_ignore_hidden(false);
219
220 assert!(config.should_watch(Path::new(".hidden.png")));
221 }
222
223 #[test]
224 fn test_clone() {
225 let config = HotReloadConfig::new().watch_extension("png");
226 let cloned = config.clone();
227
228 assert_eq!(config.enabled, cloned.enabled);
229 assert_eq!(config.extensions.len(), cloned.extensions.len());
230 }
231
232 #[test]
233 fn test_debug() {
234 let config = HotReloadConfig::new();
235 let debug = format!("{:?}", config);
236 assert!(debug.contains("HotReloadConfig"));
237 }
238}