98 lines
2.6 KiB
Markdown
98 lines
2.6 KiB
Markdown
# 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.
|
|
|
|
```text
|
|
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.
|
|
|
|
```rust
|
|
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.
|
|
|
|
```dcdl
|
|
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.
|
|
|
|
```text
|
|
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.
|
|
|
|
```rust
|
|
HostValue::object([
|
|
("name", HostValue::string_type()),
|
|
("port", HostValue::int_type().gt(443).default_int(8443)?),
|
|
])
|
|
```
|
|
|
|
Conceptually this becomes:
|
|
|
|
```text
|
|
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.
|