Hot reloading
Compiling a Unit
and a [RuntimeContext
] are expensive operations compared
to the cost of calling a function. So you should try to do this as little as
possible. It is appropriate to recompile a script when the source of the script
changes. This section provides you with details for how this can be done when
loading scripts from the filesystem.
A typical way to accomplish this is to watch a scripts directory using the
notify
crate. This allow the application to generate events whenever changes
to the directory are detected. See the hot_reloading
example and in
particular the PathReloader
type.
#[path = "hot_reloading/path_reloader.rs"]
mod path_reloader;
use std::path::PathBuf;
use std::pin::pin;
use std::sync::Arc;
use anyhow::{Context as _, Result};
use rune::{Context, Vm};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let root =
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").context("missing CARGO_MANIFEST_DIR")?);
let context = Context::with_default_modules()?;
let mut exit = pin!(tokio::signal::ctrl_c());
let mut reloader = pin!(path_reloader::PathReloader::new(
root.join("scripts"),
&context
)?);
let context = Arc::new(context.runtime()?);
let mut events = Vec::new();
loop {
tokio::select! {
_ = exit.as_mut() => {
break;
}
result = reloader.as_mut().watch(&mut events) => {
result?;
}
}
for event in events.drain(..) {
let mut vm = Vm::new(context.clone(), event.unit);
match event.kind {
path_reloader::EventKind::Added => {
if let Err(error) = vm.call(["hello"], ()) {
println!("Error: {}", error);
}
}
path_reloader::EventKind::Removed => {
if let Err(error) = vm.call(["goodbye"], ()) {
println!("Error: {}", error);
}
}
}
}
}
Ok(())
}