Skip to main content

nifty_interp/
live.rs

1//! An app whose program can be replaced while it runs, which is how `nf dev` reloads.
2
3use std::cell::RefCell;
4use std::rc::Rc;
5
6use nifty_check::hir::Program;
7use nifty_runtime::{Backend, Ui, WakeHandler};
8
9use crate::app::{App, ErrorHandler};
10use crate::machine::RuntimeError;
11
12pub struct LiveApp<B: Backend> {
13    ui: Ui<B>,
14    on_error: ErrorHandler,
15    /// `None` until a program first loads.
16    app: RefCell<Option<App<B>>>,
17}
18
19impl<B: Backend> LiveApp<B> {
20    /// An app showing nothing yet; [`LiveApp::load`] gives it a program.
21    pub fn new(backend: B, on_error: ErrorHandler) -> Self {
22        Self {
23            ui: Ui::new(backend),
24            on_error,
25            app: RefCell::new(None),
26        }
27    }
28
29    /// Shows `program` in place of the version running now, keeping each state it still
30    /// declares with the same name and type, or opens its windows if nothing runs yet.
31    ///
32    /// # Errors
33    /// When evaluating a starting value or building the UI fails. Whatever was showing
34    /// before keeps running.
35    ///
36    /// # Panics
37    /// When `program` has errors; load only programs the checker accepted.
38    pub fn load(&self, program: Program) -> Result<(), RuntimeError> {
39        let next = match &*self.app.borrow() {
40            Some(running) => running.reload(program)?,
41            None => App::start(program, self.ui.clone(), Rc::clone(&self.on_error))?,
42        };
43        *self.app.borrow_mut() = Some(next);
44        Ok(())
45    }
46
47    /// Returns something any thread can use to have `on_wake` run on the UI thread, where
48    /// it may call [`LiveApp::load`].
49    #[must_use = "dropping the waker means `on_wake` never runs"]
50    pub fn waker(&self, on_wake: WakeHandler) -> B::Waker {
51        self.ui.waker(on_wake)
52    }
53
54    /// Runs the event loop until the app quits.
55    pub fn run(&self) {
56        self.ui.run();
57    }
58}
59
60impl<B: Backend> std::fmt::Debug for LiveApp<B> {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        f.debug_struct("LiveApp").finish_non_exhaustive()
63    }
64}