1use beet::prelude::*;
2use std::num::NonZeroU8;
3use std::ops::ControlFlow;
4use std::time::Duration;
5
6
7pub struct FsApp {
21pub watcher: FsWatcher,
22}
23
24
25impl Default for FsApp {
26 fn default() -> Self {
27 Self {
30 watcher: FsWatcher {
31 filter: GlobFilter::default()
32 .with_exclude("*.git*")
33 .with_exclude("*codegen*") .with_exclude("*target*"),
35 debounce: Duration::from_millis(100),
37 ..Default::default()
38 },
39 }
40 }
41}
42
43impl FsApp {
44 pub fn runner(self) -> impl AsyncFnOnce(App) -> AppExit + 'static {
48 async |mut app| {
49 app.init();
50
51 if let Err(err) =
52 Self::on_change(&mut app, WatchEventVec::default())
53 {
54 eprintln!("Error during initial run: {}", err);
55 return AppExit::Error(NonZeroU8::new(1).unwrap());
56 }
57
58 let result: Result<AppExit> = async move {
59 let mut rx = self.watcher.watch()?;
60
61 while let Some(ev) = rx.recv().await? {
62 if ev.has_mutate() {
63 match Self::on_change(&mut app, ev)? {
64 ControlFlow::Continue(_) => {}
65 ControlFlow::Break(exit) => {
66 return Ok(exit);
67 }
68 }
69 }
70 }
71 Ok(AppExit::Success)
72 }
73 .await;
74 result.unwrap_or_else(|err| {
75 eprintln!("Error during file change: {}", err);
77 AppExit::Error(NonZeroU8::new(1).unwrap())
78 })
79 }
80 }
81
82 fn on_change(
84 app: &mut App,
85 watch_event: WatchEventVec,
86 ) -> Result<ControlFlow<AppExit>> {
87 let start = std::time::Instant::now();
88 app.world_mut().send_event_batch(
89 watch_event.take().into_iter().filter(|ev| ev.mutated()),
90 );
91 app.update();
92 let elapsed = start.elapsed();
93 debug!("App updated in {:?}", elapsed);
95 match app.should_exit() {
96 Some(exit) => Ok(ControlFlow::Break(exit)),
97 None => Ok(ControlFlow::Continue(())),
98 }
99 }
100}