# Embedding Decodal runtime は filesystem、network、environment variable を直接読み込まない。 ホストは global bindings と import の解決方法を注入し、評価結果を `Data` または application type として受け取る。 ## Rust runtime `Engine` は `ImportLoader` と global bindings を持つ。 外部 resource を必要としない source には `EmptyLoader` を使える。 ```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` は concrete value と host-defined range の両方を表す公開型である。 主な constructor は次の通りである。 - `Value::string`、`int`、`float`、`bool`、`array`、`object`: concrete value。 - `Value::string_type`、`int_type`、`float_type`、`bool_type`: primitive range。 - `Value::unknown`: 最上位 range `Unknown`。 - `Value::array_of`: 全要素へ同じ range を適用する array range。 - `Value::map_of`: 全 field value へ同じ range を適用する associative-array range。 - `Value::object_with_rest`: named fields と残りの field range を持つ object。 ## Typed Rust integration `derive` feature の `Decodal` derive は、Rust struct から `DecodalSchema` と `DecodalDecode` を生成する。 ```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()` は `Engine::bind_global` に渡せる `Value` を返す。 materialize 済みの `Data` は `DecodalDecode::decodal_decode` で Rust type に変換できる。 追加 field を受け取る struct では、map field に `#[decodal(rest)]` を付ける。 ```rust use std::collections::BTreeMap; use decodal::{Data, Decodal}; #[derive(Decodal)] struct OpenConfig { enabled: bool, #[decodal(rest)] extra: BTreeMap, } ``` `BTreeMap` は `...Unknown`、`BTreeMap` は `...String` に対応する。 named fields は rest map から除外される。 `#[decodal(rest)]` は struct ごとに1つだけ指定でき、`rename`、`default`、field constraints とは併用できない。 ## Imports `ImportLoader::load` は `LoadedImport::Source` または `LoadedImport::Value` を返す。 ホストは specifier の解決、filesystem や他の storage からの読み込み、content type の判定をすべて管理する。 `LoadedImport::Source` は DCDL source を返す。 `LoadedImport::Value` は Markdown、JSON、TOML などをホスト独自の規則で構造化して返す用途に使える。 ```text import "./post.md" -> host reads and parses Markdown -> LoadedImport::Value { key: "content/post.md", value: { frontmatter: {...}, body: "..." } } -> normal Decodal validation and materialization ``` stable `key` は import の同一性と diagnostic の provenance に使われる。 構造化した値が Decodal constraint に違反した場合、diagnostic は import key と logical value path、および違反した DCDL constraint の source span を示す。 外部形式そのものの parse error と位置情報は loader が報告する。 ## Shared host environment `HostEnvironment` は loader の作成と global binding の設定を1つにまとめる。 同じ environment を runtime、language service、LSP へ渡すことで、実行時と編集時の評価規則を一致させられる。 ```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(()) } } ``` transport 非依存の tooling は `decodal_language_service::LanguageService::new(&environment)` で構築する。 LSP integration では `decodal_lsp::LspEnvironment` を実装し、environment factory から server を起動する。 ```rust use decodal_lsp::{LspEnvironment, run_stdio}; impl LspEnvironment for AppEnvironment {} run_stdio(|initialize| { let _ = initialize; Ok(AppEnvironment) })?; ``` factory は server capability の応答前に client の `InitializeParams` を一度受け取る。 `LspEnvironment` の document lifecycle hooks を使うと、未保存の DCDL、Markdown、その他の resource を host-owned overlay に反映できる。 ## JavaScript and browser environments `decodal-wasm` の `DecodalLanguageService` は filesystem や仮想 project を仮定しない。 JavaScript 側が `globals`、`loadImport`、`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 の primitive、array、plain object は concrete value になる。 range は `$decodal` descriptor で指定する。 - `{ $decodal: 'String' | 'Int' | 'Float' | 'Bool' }` - `{ $decodal: 'Unknown' }` - `{ $decodal: 'Array', item }` - `{ $decodal: 'Map', value }` - `{ $decodal: 'Object', fields, rest }` - `{ $decodal: 'Range', constraints, default }` import callbacks は同期 API である。 network resource などの非同期入力は、評価の前に preload または cache しておく。