Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_austenite/src/watch.rs

2.1 KiB, 1 run

created by r1870400018:37573, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Poll-based recompile for the `austenite --watch` mode.
2//!
3//! There is no inotify on this fleet, so the watch samples a caller-supplied set of files on a fixed
4//! interval and rebuilds whenever any of them changes. The file set is recomputed each tick from a
5//! closure, so a chapter added to a book's `#include` list (or an asset dropped into its tree) is
6//! picked up without restarting the watch. A file appearing or disappearing counts as a change, since
7//! the snapshot keys on the paths that currently exist.
8
9use oxedyne_fe2o3_core::prelude::*;
10
11use std::collections::BTreeMap;
12use std::path::PathBuf;
13use std::time::{
14 Duration,
15 SystemTime,
16};
17
18/// Runs the poll loop: build once, then on every tick recompute the watched set via `files`, sample
19/// their modification times, and call `build` whenever the sample differs from the last one -- an
20/// mtime moved, or a watched file appeared or vanished. A build that fails returns its error, which is
21/// printed here, and the loop carries on, so a transient compile error never stops the watch. Under
22/// normal use this never returns; it ends only on an interrupt.
23pub fn run<F, G>(mut files: G, mut build: F, interval: Duration) -> Outcome<()>
24where
25 F: FnMut() -> Outcome<()>,
26 G: FnMut() -> Vec<PathBuf>,
27{
28 // Build once at the start so the watch shows a result immediately rather than on the first edit.
29 if let Err(e) = build() {
30 eprintln!("[austenite] {}", e);
31 }
32 let mut prev = snapshot(&files());
33 loop {
34 std::thread::sleep(interval);
35 let now = snapshot(&files());
36 if now != prev {
37 prev = now;
38 if let Err(e) = build() {
39 eprintln!("[austenite] {}", e);
40 }
41 }
42 }
43}
44
45/// Maps each path that currently exists and can be stat'd to its last-modified time. A path that
46/// cannot be read is simply absent from the map, so its later appearance -- or the disappearance of one
47/// present before -- changes the snapshot and triggers a rebuild.
48fn snapshot(paths: &[PathBuf]) -> BTreeMap<PathBuf, SystemTime> {
49 let mut m = BTreeMap::new();
50 for p in paths {
51 if let Ok(meta) = std::fs::metadata(p) {
52 if let Ok(t) = meta.modified() {
53 m.insert(p.clone(), t);
54 }
55 }
56 }
57 m
58}