Decodal/doc/manual/souce/design/embedding-api.md

2.6 KiB

Embedding API

Decodal core can be embedded without giving the core crate access to a filesystem. The host supplies imported sources through SourceLoader and may also provide global bindings through the host prelude API.

Host prelude

Engine owns a prelude environment. Bindings in this environment are visible from every module loaded by the engine.

prelude env
  ↓
module root env
  ↓
let / function env

Module top-level bindings shadow prelude bindings. Primitive type names such as String, Int, Float, and Bool are handled before environment lookup, so they are reserved and cannot be shadowed by host bindings.

Global bindings

The host can bind values before adding or evaluating user sources.

use decodal_core::{EmptyLoader, Engine, HostValue};

let mut engine = Engine::new(EmptyLoader);

engine.bind_global(
    "Service",
    HostValue::object([
        ("name", HostValue::string_type()),
        ("port", HostValue::int_type().gt(443).default_int(8443)?),
        ("enabled", HostValue::bool_type().default_bool(true)?),
    ]),
)?;

A user source can then refer to Service without importing it.

Service & {
  name = "api";
  port = 9443;
}

HostValue

HostValue is the public builder-facing value representation for embedding. It keeps host code from constructing internal ThunkId or ObjectValue values directly.

HostValue =
  String
  Int
  Float
  Bool
  Array(Vec<HostValue>)
  Object(Vec<HostField>)
  Abstract { constraints, default }

When a host value is bound, the engine internalizes it into RuntimeValue and allocates value thunks for object fields, array items, and defaults.

Abstract host objects

A host-provided schema object is represented as a concrete object structure whose fields may contain abstract values.

HostValue::object([
    ("name", HostValue::string_type()),
    ("port", HostValue::int_type().gt(443).default_int(8443)?),
])

Conceptually this becomes:

Concrete(Object {
  name -> Thunk(Abstract { constraints: [String], default: none })
  port -> Thunk(Abstract { constraints: [Int, > 443], default: 8443 })
})

This matches the runtime model used for Decodal source-defined schema objects.

SourceLoader and prelude together

SourceLoader and host prelude bindings are independent mechanisms.

  • Use SourceLoader when user sources should explicitly import host-provided modules.
  • Use prelude bindings when host-provided schemas or constants should be globally available.

Both mechanisms share the same runtime evaluator, thunk model, and materialization rules.