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(())
}