Skip to main content

nifty_rt/
lib.rs

1//! The runtime library compiled Nifty apps link against.
2//!
3//! `nf build` compiles a program to machine code that calls the `nf_*` functions
4//! [`symbols`] names, and emits a [`ProgramDesc`] describing its states, windows and
5//! views. The program's `main` hands that descriptor to [`nifty_rt_main`], which shows the
6//! windows on the platform's native UI through the same engine the interpreter uses.
7
8mod abi;
9mod context;
10mod dialog;
11mod download;
12mod ffi;
13mod logic;
14mod lower;
15mod place;
16mod program;
17mod show;
18pub mod symbols;
19mod value;
20mod window;
21
22use std::process;
23use std::rc::Rc;
24
25use nifty_runtime::{AppTemplate, Backend, Engine, ErrorHandler, Ui};
26
27pub use context::{AppError, Context, SourceLocation};
28pub use download::{DownloadBinding, DownloadDesc};
29pub use ffi::{Slice, Str};
30pub use logic::{Binding, Compiled, Frame, Region};
31pub use place::{PathStep, PlaceDesc, StepKind};
32pub use program::{
33    ABI_VERSION, ActionFn, BindingDesc, ExpandFn, FrameDesc, InitFn, MenuDesc, MenuEntryDesc,
34    MenuEntryKind, ProgramDesc, ReadFn, RegionDesc, ShortcutDesc, SpanDesc, StateDesc,
35    TemplateDesc, TemplateKind, ViewDesc, WindowDesc, WriteFn,
36};
37pub use show::Sink;
38pub use value::{BOOL, FLOAT, INT, STRING, Slot, TypeDesc, TypeKind};
39
40/// The exit status of an app that stopped with an error.
41const EXIT_FAILED: i32 = 1;
42
43/// The system libraries an app linked with this runtime needs, in the linker's own
44/// syntax, as `rustc --print native-static-libs` reports them for the static library,
45/// less `-lSystem`, which the C compiler driver always links. `nf build`'s link test
46/// fails when one is missing.
47#[cfg(target_os = "macos")]
48pub const NATIVE_LIBS: &[&str] = &[
49    "-lc",
50    "-lm",
51    "-liconv",
52    "-lobjc",
53    "-framework",
54    "AppKit",
55    "-framework",
56    "CloudKit",
57    "-framework",
58    "CoreData",
59    "-framework",
60    "CoreFoundation",
61    "-framework",
62    "CoreGraphics",
63    "-framework",
64    "CoreImage",
65    "-framework",
66    "CoreText",
67    "-framework",
68    "CoreVideo",
69    "-framework",
70    "Foundation",
71    "-framework",
72    "QuartzCore",
73    "-framework",
74    "Security",
75];
76
77/// See the macOS list; Windows apps also link the C runtime statically, as `nf` does.
78#[cfg(windows)]
79pub const NATIVE_LIBS: &[&str] = &[
80    "kernel32.lib",
81    "ntdll.lib",
82    "userenv.lib",
83    "ws2_32.lib",
84    "dbghelp.lib",
85    "/defaultlib:libcmt",
86];
87
88/// A program whose descriptor checked out and whose state is set up, ready to show.
89#[derive(Debug)]
90pub struct Prepared {
91    app: AppTemplate<Compiled>,
92    views: Vec<logic::ViewTemplate>,
93    context: Context,
94}
95
96/// Checks `program`'s descriptor and sets up its state, before any UI exists.
97///
98/// # Errors
99/// When the descriptor is malformed, or setting up state fails.
100pub fn prepare(program: &'static ProgramDesc) -> Result<Prepared, AppError> {
101    let lower::Lowered { app, views } = lower::lower(program)?;
102    let context = Context::new(program)?;
103    Ok(Prepared {
104        app,
105        views,
106        context,
107    })
108}
109
110impl Prepared {
111    /// Shows the program's windows on `ui`. Errors during events go to `on_error`.
112    ///
113    /// # Errors
114    /// When building the first UI fails.
115    pub fn show<B: Backend>(
116        self,
117        ui: Ui<B>,
118        on_error: ErrorHandler<AppError>,
119    ) -> Result<Rc<Engine<Compiled, B>>, AppError> {
120        let Self {
121            app,
122            views,
123            context,
124        } = self;
125        Engine::start(ui, app, on_error, |ui, engine| {
126            Compiled::new(context, views, ui, engine)
127        })
128    }
129}
130
131/// Runs `program` until the app quits, and returns the process's exit status. An error
132/// stops the app, since it leaves the app half-way through a change.
133///
134/// # Safety
135/// `program` points to a descriptor `nf build` emitted with this runtime's
136/// [`ABI_VERSION`], next to the compiled code it describes, and this runs on the main
137/// thread.
138#[unsafe(no_mangle)]
139pub unsafe extern "C" fn nifty_rt_main(program: *const ProgramDesc) -> i32 {
140    // SAFETY: by the caller's contract, the descriptor lives as long as the program.
141    let Some(program) = (unsafe { program.as_ref() }) else {
142        eprintln!("{}", AppError::malformed("its program is missing"));
143        return EXIT_FAILED;
144    };
145    let on_error = Rc::new(|error: AppError| {
146        eprintln!("{error}");
147        process::exit(EXIT_FAILED);
148    });
149    let shown = prepare(program)
150        .and_then(|prepared| prepared.show(Ui::new(nifty_platform::platform_backend()), on_error));
151    match shown {
152        Ok(engine) => {
153            engine.run();
154            0
155        }
156        Err(error) => {
157            eprintln!("{error}");
158            EXIT_FAILED
159        }
160    }
161}
162
163#[cfg(test)]
164mod tests;