elly_host/lib.rs
1//! What every Elly embedder wants and `elly-core` does not have to know about:
2//! the official file-system module resolver ([`FsResolver`]), the `IO`
3//! capability modules ([`io`]), and running a program's `__main` against them
4//! ([`Io::run`]). See `docs/done/2026-10-02_elly-host.md`.
5//!
6//! Everything that touches the outside world goes through [`Sys`], so a host
7//! without a real OS (the wasm playground) can supply its own. [`StdSys`] is the
8//! one `std` implementation, for native targets and `wasm32-wasip1`.
9
10mod io;
11mod resolve;
12
13use std::io::Write;
14use std::path::{Path, PathBuf};
15
16pub use io::{io, Io, Run};
17pub use resolve::{is_relative_spec, search_path, FsResolver, PATH_VAR};
18
19/// Which standard stream a write goes to.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Stream {
22 Out,
23 Err,
24}
25
26/// The system surface a host offers Elly: output, module sources, the
27/// environment and the way out. The resolver and `IO` reach the outside world
28/// only through this.
29pub trait Sys {
30 /// Write `s` to `stream` and flush it.
31 fn write(&self, stream: Stream, s: &str) -> std::io::Result<()>;
32
33 /// The canonical form of `path`, with symlinks resolved, or `None` if there
34 /// is nothing there. A resolved module's canonical name is its real path.
35 fn real_path(&self, path: &Path) -> Option<PathBuf>;
36
37 /// The text of the file at `path`, or `None` if it is not a readable UTF-8
38 /// file.
39 fn read_source(&self, path: &Path) -> Option<String>;
40
41 /// The environment variable `name`, if it is set and UTF-8.
42 fn var(&self, name: &str) -> Option<String>;
43
44 /// The whole environment. Entries whose name or value is not UTF-8 are
45 /// dropped: Elly strings are UTF-8, and a map that silently lost a key is
46 /// better than a startup that refuses to run.
47 fn vars(&self) -> Vec<(String, String)>;
48
49 /// End the process with `code`, immediately.
50 fn exit(&self, code: i32) -> !;
51}
52
53/// [`Sys`] over the standard library.
54pub struct StdSys;
55
56impl Sys for StdSys {
57 fn write(&self, stream: Stream, s: &str) -> std::io::Result<()> {
58 fn write_to(out: &mut impl Write, s: &str) -> std::io::Result<()> {
59 out.write_all(s.as_bytes())?;
60 out.flush()
61 }
62 match stream {
63 Stream::Out => write_to(&mut std::io::stdout().lock(), s),
64 Stream::Err => write_to(&mut std::io::stderr().lock(), s),
65 }
66 }
67
68 fn real_path(&self, path: &Path) -> Option<PathBuf> {
69 path.canonicalize().ok()
70 }
71
72 fn read_source(&self, path: &Path) -> Option<String> {
73 std::fs::read_to_string(path).ok()
74 }
75
76 fn var(&self, name: &str) -> Option<String> {
77 std::env::var(name).ok()
78 }
79
80 fn vars(&self) -> Vec<(String, String)> {
81 // `vars()` would *panic* on a non-UTF-8 entry, which would make a
82 // program's ability to start depend on someone else's environment.
83 std::env::vars_os()
84 .filter_map(|(k, v)| Some((k.into_string().ok()?, v.into_string().ok()?)))
85 .collect()
86 }
87
88 fn exit(&self, code: i32) -> ! {
89 std::process::exit(code)
90 }
91}