Capability Functions

Capability functions are Rust functions the host exposes to scripts. Scripts import them by package name.

Defining a capability function🔗

use dogma_vm::{NativeSpec, Value};

let spec = NativeSpec::new(
    "greet",          // function name as seen in scripts
    1,                // arity (number of arguments)
    |args| match &args[0] {
        Value::String(name) => Ok(Value::String(format!("Hello, {name}!"))),
        _ => Err("greet: expected String".into()),
    },
)
.returns("String");

Registering with the engine🔗

Engine::builder()
    .with_capability_functions("myapp", vec![spec])
    .build()

In scripts:

import { greet } from "myapp"

fn main() {
    let msg = greet("Aria")
    println(msg)
}

Argument types🔗

Use pattern matching on the Value enum:

match &args[0] {
    Value::I32(n) => { /* i32 */ }
    Value::I64(n) => { /* i64 */ }
    Value::F32(f) => { /* f32 */ }
    Value::F64(f) => { /* f64 */ }
    Value::String(s) => { /* String */ }
    Value::Bool(b) => { /* bool */ }
    Value::Unit => { /* () */ }
    other => Err(format!("unexpected argument: {other}").into()),
}

Returning errors🔗

Return Err(String) to surface an error to the script as a Dogma Err value:

NativeSpec::new("safe_div", 2, |args| {
    let a = match &args[0] { Value::I64(n) => *n, _ => return Err("expected i64".into()) };
    let b = match &args[1] { Value::I64(n) => *n, _ => return Err("expected i64".into()) };
    if b == 0 { return Err("division by zero".into()); }
    Ok(Value::I64(a / b))
})
.returns("i64")

Built-in virtual packages🔗

The stdlib virtual packages (dogma:core, dogma:fs, etc.) are registered the same way. The host chooses which to include:

Engine::builder()
    .with_capability_functions("dogma:core", dogma_vm::stdlib::core_natives())
    .with_capability_functions("dogma:fs", dogma_vm::stdlib::fs_natives())
    // dogma:net not included — scripts cannot use networking
    .build()