Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Embed mq in Rust

Add mq-lang to your Cargo.toml:

[dependencies]
mq-lang = "0.9"

Create an engine once, compile a query, and run it with each input:

use mq_lang::{DefaultEngine, RuntimeValue, parse_text_input};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut engine = DefaultEngine::default();
    engine.load_builtin_module();
    engine.register_fn("double", |n: i64| Ok(n * 2));

    let query = engine.compile("double(21)")?;
    let input = parse_text_input("ignored")?;
    let output = engine.eval_compiled(&query, input.into_iter())?;
    assert_eq!(output.values(), &[RuntimeValue::Number(42.into())]);
    Ok(())
}

compile parses the query once. The VM compiles and caches its bytecode on the first evaluation, then reuses it when possible. Each input can be parsed with parse_markdown_input, parse_text_input, or another input helper.

For filesystem modules, pass one Io to both query evaluation and module loading. The sandbox denies access by default; grant only the paths your application needs:

#![allow(unused)]
fn main() {
use mq_lang::{Engine, NativeIo, SandboxedIo, Shared};
use std::path::PathBuf;

let io = Shared::new(
    SandboxedIo::new(NativeIo::default())
        .allow_read(vec![PathBuf::from("./queries")]),
);
let mut engine = Engine::with_default_io(io);
engine.set_search_paths(vec![PathBuf::from("./queries")]);
engine.load_builtin_module();
}

Use Engine::with_io(resolver, io) if your application supplies its own module resolver. Engine::set_timeout and Engine::set_max_call_stack_depth can limit query execution.

print, stderr, and input go through the same Io. Override write_stdout_line, write_stderr_line, and read_stdin_line to keep them away from the terminal, for example in a TUI.

halt(code) does not exit the process. It returns an error that try cannot catch, and Error::exit_code gives the requested code:

#![allow(unused)]
fn main() {
match engine.eval("halt(3)", input.into_iter()) {
    Err(err) if err.exit_code().is_some() => { /* stop, or exit with the code */ }
    other => { /* ... */ }
}
}

Reading results

RuntimeValues::into_markdown_nodes turns results into Markdown nodes, rendering non-Markdown values as text. For plain values, use as_str, as_f64, as_bool, as_array, as_dict, and get:

#![allow(unused)]
fn main() {
let title = value.get("title").and_then(|v| v.as_str());
}

mq_lang::from_value deserializes a value into any serde::Deserialize type:

#![allow(unused)]
fn main() {
#[derive(serde::Deserialize)]
struct Section {
    title: String,
    codes: Vec<String>,
}

let sections: Vec<Section> = output
    .values()
    .iter()
    .map(mq_lang::from_value)
    .collect::<Result<_, _>>()?;
}