Skip to main content

nifty_interp/
app.rs

1//! Runs a checked program's windows on the runtime's engine, with the interpreter giving
2//! every dynamic part its value.
3
4use std::rc::Rc;
5
6use nifty_check::hir::Program;
7use nifty_runtime::{Backend, Engine, Ui};
8
9use crate::logic::Interpreter;
10use crate::lower;
11use crate::machine::{Machine, Result, RuntimeError};
12
13pub type ErrorHandler = nifty_runtime::ErrorHandler<RuntimeError>;
14
15/// A running app: its state, its windows, and what each widget shows.
16pub struct App<B: Backend> {
17    engine: Rc<Engine<Interpreter, B>>,
18}
19
20impl<B: Backend> std::fmt::Debug for App<B> {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_struct("App").finish_non_exhaustive()
23    }
24}
25
26impl<B: Backend> App<B> {
27    /// Sets up state and shows every window.
28    ///
29    /// # Errors
30    /// When evaluating a starting value or building the first UI fails.
31    ///
32    /// # Panics
33    /// When `program` has errors; run only programs the checker accepted.
34    pub fn start(program: Program, ui: Ui<B>, on_error: ErrorHandler) -> Result<Self> {
35        let machine = Machine::new(Rc::new(program), None)?;
36        let app = lower::app(machine.program());
37        let engine = Engine::start(ui, app, on_error, |ui, engine| {
38            Interpreter::new(machine, ui, engine)
39        })?;
40        Ok(Self { engine })
41    }
42
43    /// Starts `program` in this app's windows, in place of this app. Each state it still
44    /// declares with the same name and type keeps its value; the others start over.
45    ///
46    /// # Errors
47    /// When evaluating a starting value or building the new UI fails. Nothing has changed
48    /// then, and this app keeps running.
49    ///
50    /// # Panics
51    /// When `program` has errors; run only programs the checker accepted.
52    pub fn reload(&self, program: Program) -> Result<Self> {
53        let machine = Machine::new(Rc::new(program), Some(self.engine.logic().machine()))?;
54        let app = lower::app(machine.program());
55        let engine = self
56            .engine
57            .reload(app, |ui, engine| Interpreter::new(machine, ui, engine))?;
58        Ok(Self { engine })
59    }
60
61    /// Runs the event loop until the app quits.
62    pub fn run(&self) {
63        self.engine.run();
64    }
65
66    #[cfg(test)]
67    pub(crate) fn machine(&self) -> &Machine {
68        self.engine.logic().machine()
69    }
70
71    /// Waits until every running download has sent its last report, without applying them.
72    #[cfg(test)]
73    pub(crate) fn wait_for_downloads(&self) {
74        self.engine.logic().wait_for_downloads();
75    }
76
77    /// Waits until a download sends a report, and wakes the UI thread to apply it.
78    #[cfg(test)]
79    pub(crate) fn wait_for_download_report(&self) {
80        self.engine.logic().wait_for_download_report();
81    }
82}