# Embedding The Decodal runtime does not read filesystems, networks, or environment variables directly. The host injects global bindings and import resolution, then receives the evaluation result as `Data` or an application type. ## Rust runtime `Engine` owns an `ImportLoader` and global bindings. Use `EmptyLoader` for source that does not require external resources. ```rust use decodal::{EmptyLoader, Engine, Value}; let mut engine = Engine::new(EmptyLoader); engine.bind_global( "Service", Value::object([ ("name", Value::string_type()), ("port", Value::int_type().gt(443).default_int(8443)?), ("enabled", Value::bool_type().default_bool(true)?), ]), )?; let module = engine.add_root_source( "service.dcdl", "service.dcdl", r#"{ name = "api"; port = 9443; } as Service"#, )?; let value = engine.eval_module(module)?; let data = engine.materialize(&value)?; ``` `Value` is the public type for both concrete values and host-defined ranges. Its main constructors are: - `Value::string`, `int`, `float`, `bool`, `array`, and `object`: concrete values. - `Value::string_type`, `int_type`, `float_type`, and `bool_type`: primitive ranges. - `Value::unknown`: the top range, `Unknown`. - `Value::array_of`: an array range that applies one range to every element. - `Value::map_of`: an associative-array range that applies one range to every field value. - `Value::object_with_rest`: an object with named fields and a range for remaining fields. ## Typed Rust integration The `Decodal` derive from the `derive` feature generates `DecodalSchema` and `DecodalDecode` implementations from a Rust struct. ```rust use decodal::{Decodal, DecodalDecode, DecodalSchema}; #[derive(Decodal)] struct Service { name: String, #[decodal(gt = 443, default = 8443)] port: i64, #[decodal(rename = "feature.enable", default = true)] feature_enabled: bool, } ``` `DecodalSchema::decodal_schema()` returns a `Value` suitable for `Engine::bind_global`. Materialized `Data` can be converted to a Rust type with `DecodalDecode::decodal_decode`. For a struct that accepts additional fields, mark a map field with `#[decodal(rest)]`. ```rust use std::collections::BTreeMap; use decodal::{Data, Decodal}; #[derive(Decodal)] struct OpenConfig { enabled: bool, #[decodal(rest)] extra: BTreeMap, } ``` `BTreeMap` corresponds to `...Unknown`, while `BTreeMap` corresponds to `...String`. Named fields are excluded from the rest map. Only one `#[decodal(rest)]` field is allowed per struct, and it cannot be combined with `rename`, `default`, or field constraints. ## Imports `ImportLoader::load` returns either `LoadedImport::Source` or `LoadedImport::Value`. The host controls specifier resolution, reading from the filesystem or other storage, and content-type detection. `LoadedImport::Source` supplies DCDL source. `LoadedImport::Value` is intended for structured host-defined representations of formats such as Markdown, JSON, and TOML. ```text import "./post.md" -> host reads and parses Markdown -> LoadedImport::Value { key: "content/post.md", value: { frontmatter: {...}, body: "..." } } -> normal Decodal validation and materialization ``` A stable `key` identifies an import and supplies diagnostic provenance. If a structured value violates a Decodal constraint, the diagnostic identifies the import key, logical value path, and source span of the violated DCDL constraint. The loader reports parse errors and positions in the external format itself. ## Shared host environment `HostEnvironment` combines loader construction and global-binding setup. Passing the same environment to the runtime, language service, and LSP keeps runtime and editor evaluation rules aligned. ```rust use decodal::{Engine, HostEnvironment}; struct AppEnvironment; impl HostEnvironment for AppEnvironment { type Loader = ContentLoader; fn create_loader(&self) -> Self::Loader { ContentLoader::new() } fn configure_engine( &self, engine: &mut Engine, ) -> decodal::Result<()> { engine.bind_global("Site", site_schema())?; Ok(()) } } ``` Construct transport-independent tooling with `decodal_language_service::LanguageService::new(&environment)`. For LSP integration, implement `decodal_lsp::LspEnvironment` and start the server from an environment factory. ```rust use decodal_lsp::{LspEnvironment, run_stdio}; impl LspEnvironment for AppEnvironment {} run_stdio(|initialize| { let _ = initialize; Ok(AppEnvironment) })?; ``` The factory receives the client's `InitializeParams` once, before the server responds with its capabilities. Use the `LspEnvironment` document lifecycle hooks to reflect unsaved DCDL, Markdown, and other resources in a host-owned overlay. ## JavaScript and browser environments The `DecodalLanguageService` in `decodal-wasm` does not assume a filesystem or virtual project. JavaScript supplies `globals`, `loadImport`, and `completeImport`. ```js import init, { DecodalLanguageService } from 'decodal-wasm'; await init(); const files = { 'schema.dcdl': 'Server = { port = Int; };', }; const service = new DecodalLanguageService({ globals: { App: { enabled: { $decodal: 'Bool', default: true }, }, }, loadImport(_currentKey, specifier) { const key = specifier.replace(/^\.\//, ''); return { kind: 'source', key, name: key, source: files[key] }; }, completeImport() { return ['./schema.dcdl']; }, }); const result = service.evaluate('main.dcdl', 'main.dcdl', 'App'); ``` JavaScript primitives, arrays, and plain objects become concrete values. Use `$decodal` descriptors for ranges: - `{ $decodal: 'String' | 'Int' | 'Float' | 'Bool' }` - `{ $decodal: 'Unknown' }` - `{ $decodal: 'Array', item }` - `{ $decodal: 'Map', value }` - `{ $decodal: 'Object', fields, rest }` - `{ $decodal: 'Range', constraints, default }` Import callbacks are synchronous. Preload or cache asynchronous inputs such as network resources before evaluation.