1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use super::*;

pub struct Hot<T: Load> {
    current: RefCell<T>,
    manager: Manager,
    path: PathBuf,
    options: T::Options,
    need_update: Arc<std::sync::atomic::AtomicBool>,
    update: RefCell<Option<Future<T>>>,
    #[cfg(not(target_arch = "wasm32"))]
    #[allow(dead_code)] // This is here for delaying the drop of the watcher
    watcher: Option<notify::RecommendedWatcher>,
}

pub type Ref<'a, T> = std::cell::Ref<'a, T>;

impl<T: Load> Hot<T> {
    pub fn get(&self) -> Ref<T> {
        if let Ok(mut current) = self.current.try_borrow_mut() {
            let mut update = self.update.borrow_mut();
            if let Some(future) = &mut *update {
                if let std::task::Poll::Ready(result) = future.as_mut().poll(
                    &mut std::task::Context::from_waker(futures::task::noop_waker_ref()),
                ) {
                    *update = None;
                    match result {
                        Ok(new) => *current = new,
                        Err(e) => log::error!("{e}"),
                    }
                    self.need_update
                        .store(false, std::sync::atomic::Ordering::SeqCst);
                }
            } else if self.need_update.load(std::sync::atomic::Ordering::SeqCst) {
                *update = Some(
                    self.manager
                        .load_with(&self.path, &self.options)
                        .boxed_local(),
                )
            }
        }
        self.current.borrow()
    }
}

impl<T: Load> Load for Hot<T> {
    type Options = T::Options;
    fn load(manager: &Manager, path: &Path, options: &Self::Options) -> Future<Self> {
        let manager = manager.clone();
        let path = path.to_owned();
        let options = options.clone();
        let need_update = Arc::new(std::sync::atomic::AtomicBool::new(false));
        #[cfg(not(target_arch = "wasm32"))]
        let watcher = if manager.hot_reload_enabled() {
            use notify::Watcher;
            let need_update = need_update.clone();
            let mut watcher =
                notify::recommended_watcher(move |result: notify::Result<notify::Event>| {
                    let event = result.unwrap();
                    if event.kind.is_modify() {
                        need_update.store(true, std::sync::atomic::Ordering::SeqCst);
                    }
                })
                .unwrap();
            watcher
                .watch(&path, notify::RecursiveMode::Recursive)
                .unwrap();
            log::info!("watching {path:?}");
            Some(watcher)
        } else {
            None
        };
        async move {
            let initial = manager.load_with(&path, &options).await?;
            Ok(Self {
                need_update,
                options,
                manager: manager.clone(),
                path: path.to_owned(),
                current: RefCell::new(initial),
                update: RefCell::new(None),
                #[cfg(not(target_arch = "wasm32"))]
                watcher,
            })
        }
        .boxed_local()
    }
    const DEFAULT_EXT: Option<&'static str> = T::DEFAULT_EXT;
}