use std::path::PathBuf;
use log::error;
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher};
type Event = notify::Result<notify::Event>;
#[derive(Debug)]
pub struct FileChangeWatcher {
paths_to_watch: Vec<PathBuf>,
_watcher: RecommendedWatcher,
events_rx: std::sync::mpsc::Receiver<Event>,
}
impl FileChangeWatcher {
pub fn new(files: &[&str]) -> Self {
let paths_to_watch: Vec<PathBuf> = files.iter().map(|s| s.parse().unwrap()).collect();
let (events_tx, events_rx) = std::sync::mpsc::channel::<Event>();
let mut watcher = RecommendedWatcher::new(
move |res| {
let _ = events_tx.send(res);
},
notify::Config::default(),
)
.expect("could not create Watcher");
for path in paths_to_watch.iter() {
_ = watcher.watch(path, RecursiveMode::NonRecursive);
}
FileChangeWatcher {
paths_to_watch,
_watcher: watcher,
events_rx,
}
}
pub fn check_for_changes(&self) -> Option<Vec<&PathBuf>> {
let mut result: Vec<&PathBuf> = vec![];
while let Ok(event) = self.events_rx.try_recv() {
if let Ok(event) = event {
if let notify::Event {
kind: EventKind::Modify(_),
paths,
attrs: _,
} = event
{
for p in paths {
for q in self.paths_to_watch.iter() {
let path_equals = p
.as_path()
.to_str()
.expect("Path should be utf8")
.ends_with(q.to_str().expect("Path should be utf8"));
if path_equals {
result.push(q);
}
}
}
}
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
}
#[derive(Debug)]
pub struct ShaderFileWatcher {
wgsl_file: PathBuf,
watcher: FileChangeWatcher,
}
impl ShaderFileWatcher {
pub fn new(path: &str) -> Self {
let wgsl_file: PathBuf = path.parse().expect("invalid path");
if !wgsl_file.exists() {
error!("Wgsl file at {wgsl_file:?} path not found!");
}
Self {
wgsl_file,
watcher: FileChangeWatcher::new(&[path]),
}
}
}
impl ShaderFileWatcher {
pub fn check_for_changes(&self) -> Option<String> {
if let Some(e) = self.watcher.check_for_changes() {
if !e.contains(&&self.wgsl_file) {
return None;
}
let wgsl = std::fs::read_to_string(&self.wgsl_file).unwrap();
if wgsl.trim().is_empty() {
return None;
}
if let Err(err) = wgpu::naga::front::wgsl::parse_str(&wgsl) {
error!("WGSL at {:?} is invalid: {err}", self.wgsl_file);
} else {
println!("Hot reloaded WGSL from {:?}", self.wgsl_file);
return Some(wgsl);
}
}
None
}
}