Make decodal the published library crate

This commit is contained in:
Keisuke Hirata 2026-06-25 22:51:16 +09:00
parent 6e2363632b
commit 87fef44c68
No known key found for this signature in database
20 changed files with 159 additions and 96 deletions

8
Cargo.lock generated
View File

@ -27,21 +27,21 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
name = "decodal"
version = "0.1.0"
dependencies = [
"decodal-core",
"regex",
]
[[package]]
name = "decodal-core"
name = "decodal-cli"
version = "0.1.0"
dependencies = [
"regex",
"decodal",
]
[[package]]
name = "decodal-wasm"
version = "0.1.0"
dependencies = [
"decodal-core",
"decodal",
"serde_json",
"wasm-bindgen",
]

View File

@ -2,40 +2,39 @@
Decodal is a small deterministic DSL for describing, composing, validating, and materializing structured data.
It is designed around a lightweight Rust core:
It is designed around a lightweight Rust library:
- host-supplied imports through `SourceLoader`
- no filesystem access in `decodal-core`
- no filesystem access in the library core
- concrete and abstract values with constraints and defaults
- deterministic expression evaluation
- optional regex support behind a Cargo feature
- browser playground support through WebAssembly
## Install the CLI
## Library crate
```sh
cargo install decodal
Embedded hosts should depend on `decodal` and provide imports with a `SourceLoader`.
```toml
[dependencies]
decodal = "0.1"
```
Run a Decodal file:
## CLI
A standalone CLI is kept in this repository as the `decodal-cli` workspace package.
It builds a `decodal` binary, but it is not the primary crates.io package.
Run a Decodal file from the repository:
```sh
decodal examples/advanced/main.dcdl
cargo run -q -p decodal-cli -- examples/advanced/main.dcdl
```
Enable optional regex support when needed:
```sh
cargo install decodal --features regex
```
## Library crate
Embedded hosts should depend on `decodal-core` and provide imports with a `SourceLoader`.
```toml
[dependencies]
decodal-core = "0.1"
cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl
```
## Web playground

View File

@ -1,5 +1,5 @@
[package]
name = "decodal"
name = "decodal-cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
@ -9,10 +9,15 @@ readme.workspace = true
description = "Command-line interface for the Decodal data description language."
keywords = ["decodal", "dsl", "config", "cli"]
categories = ["command-line-utilities", "config"]
publish = false
[[bin]]
name = "decodal"
path = "src/main.rs"
[features]
default = []
regex = ["decodal-core/regex"]
regex = ["decodal/regex"]
[dependencies]
decodal-core = { version = "0.1.0", path = "../decodal-core" }
decodal = { version = "0.1.0", path = "../decodal-core" }

View File

@ -4,7 +4,7 @@ use std::{
process::ExitCode,
};
use decodal_core::{
use decodal::{
Data, Diagnostic, DiagnosticKind, Engine, LoadedSource, SourceId, SourceLoader, Span,
format_diagnostic_with,
};
@ -28,7 +28,7 @@ fn run() -> Result<(), String> {
Ok(())
}
"--version" | "-V" => {
println!("Decodal {}", decodal_core::version());
println!("Decodal {}", decodal::version());
Ok(())
}
"check" => {

View File

@ -1,12 +1,12 @@
[package]
name = "decodal-core"
name = "decodal"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
readme.workspace = true
description = "Core parser, evaluator, and embedding API for the Decodal data description language."
description = "Parser, evaluator, and embedding API for the Decodal data description language."
keywords = ["decodal", "dsl", "config", "schema"]
categories = ["config", "parser-implementations"]

View File

@ -1,6 +1,6 @@
use decodal_core::{Data, EmptyLoader, Engine, HostValue};
use decodal::{Data, EmptyLoader, Engine, HostValue};
fn main() -> decodal_core::Result<()> {
fn main() -> decodal::Result<()> {
let mut engine = Engine::new(EmptyLoader);
engine.bind_global(

View File

@ -9,12 +9,13 @@ readme.workspace = true
description = "WebAssembly wrapper for evaluating Decodal in browser playgrounds."
keywords = ["decodal", "wasm", "dsl", "config"]
categories = ["wasm", "config"]
publish = false
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
decodal-core = { version = "0.1.0", path = "../decodal-core" }
decodal = { version = "0.1.0", path = "../decodal-core" }
serde_json.workspace = true
wasm-bindgen.workspace = true

View File

@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use decodal_core::{
use decodal::{
Data, Diagnostic, DiagnosticKind, EmptyLoader, Engine, LoadedSource, SourceId, SourceLoader,
Span, format_diagnostic_with,
};
@ -81,7 +81,7 @@ impl SourceLoader for VirtualLoader {
&mut self,
current_key: Option<&str>,
specifier: &str,
) -> decodal_core::Result<LoadedSource> {
) -> decodal::Result<LoadedSource> {
let key = resolve_import(current_key, specifier).ok_or_else(|| {
Diagnostic::new(
DiagnosticKind::Import,
@ -139,7 +139,7 @@ fn normalize_path(path: &str) -> Option<String> {
}
}
fn format_diagnostic_with_root(diagnostic: &decodal_core::Diagnostic, root_name: &str) -> String {
fn format_diagnostic_with_root(diagnostic: &decodal::Diagnostic, root_name: &str) -> String {
format_diagnostic_with(diagnostic, |source| {
(source == SourceId(0)).then_some(root_name)
})

View File

@ -24,7 +24,7 @@ Primitive type names such as `String`, `Int`, `Float`, and `Bool` are handled be
The host can bind values before adding or evaluating user sources.
```rust
use decodal_core::{EmptyLoader, Engine, HostValue};
use decodal::{EmptyLoader, Engine, HostValue};
let mut engine = Engine::new(EmptyLoader);

View File

@ -37,7 +37,7 @@ primitive type conflict や明らかな numeric bound conflict は合成時に
## Core defaults
`decodal-core` defaults to `std` only.
`decodal` defaults to `std` only.
```toml
[features]
@ -46,7 +46,7 @@ std = []
regex = ["std", "dep:regex"]
```
Building `decodal-core` with `--no-default-features` keeps the core in `no_std + alloc` mode and avoids optional dependencies.
Building `decodal` with `--no-default-features` keeps the core in `no_std + alloc` mode and avoids optional dependencies.
## Regex
@ -54,7 +54,7 @@ Regex constraints are implemented behind the `regex` feature.
When the feature is disabled, regex constraints parse and compose, but validating a concrete value against them returns an unsupported feature diagnostic.
```sh
cargo run -q -p decodal --features regex -- examples/regex/main.dcdl
cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl
```
Regex constraints are accumulated during `&` composition.
@ -63,5 +63,5 @@ Concrete strings must match every regex constraint attached to the abstract valu
## CLI features
`decodal-cli` exposes a matching `regex` feature that enables `decodal-core/regex`.
`decodal-cli` exposes a matching `regex` feature that enables `decodal/regex`.
The feature is not enabled by default so the default CLI binary remains small.

View File

@ -9,27 +9,25 @@ Run the normal Rust checks from the repository root.
```sh
cargo fmt --check
cargo test
cargo check -p decodal-core --no-default-features
cargo check -p decodal --no-default-features
nix flake check
```
Regex support is optional and should be tested explicitly when touched.
```sh
cargo test -p decodal-core --features regex
cargo run -q -p decodal --features regex -- examples/regex/main.dcdl
cargo test -p decodal --features regex
cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl
```
## crates.io release
Crates are published as versioned workspace packages under the dual license `MIT OR Apache-2.0`.
The dependency order matters because `decodal` and `decodal-wasm` depend on `decodal-core` from crates.io.
The primary crates.io package is `decodal`, which contains the embeddable library.
Workspace support crates such as `decodal-cli` and `decodal-wasm` are not published for 0.1.0.
The project is dual licensed as `MIT OR Apache-2.0`.
```sh
cargo publish -p decodal-core
# wait until crates.io index has decodal-core 0.1.0
cargo publish -p decodal
cargo publish -p decodal-wasm
```
Before publishing, run:
@ -37,12 +35,10 @@ Before publishing, run:
```sh
cargo fmt --check
cargo test
cargo check -p decodal-core --no-default-features
cargo publish -p decodal-core --dry-run
cargo check -p decodal --no-default-features
cargo publish -p decodal --dry-run
```
`cargo publish --dry-run` for `decodal` and `decodal-wasm` will fail until the matching `decodal-core` version is already visible in the crates.io index.
## Web site and playground
The Astro documentation site and browser playground are kept in:

View File

@ -16,7 +16,7 @@ This example exercises multiple current Decodal features together:
Run it with:
```sh
cargo run -q -p decodal -- examples/advanced/main.dcdl
cargo run -q -p decodal-cli -- examples/advanced/main.dcdl
```
The entrypoint is `main.dcdl`.

View File

@ -3,7 +3,7 @@
Regex constraints are optional. Run this example with the `regex` feature enabled:
```sh
cargo run -q -p decodal --features regex -- examples/regex/main.dcdl
cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl
```
Without the feature, regex validation returns an unsupported feature diagnostic.

View File

@ -37,7 +37,7 @@ rustPlatform.buildRustPackage {
cargoBuildFlags = [
"-p"
"decodal"
"decodal-cli"
];
doInstallCheck = true;

View File

@ -0,0 +1,55 @@
# Decodal
Decodal is a small deterministic DSL for describing, composing, validating, and materializing structured data.
It is designed around a lightweight Rust library:
- host-supplied imports through `SourceLoader`
- no filesystem access in the library core
- concrete and abstract values with constraints and defaults
- deterministic expression evaluation
- optional regex support behind a Cargo feature
- browser playground support through WebAssembly
## Library crate
Embedded hosts should depend on `decodal` and provide imports with a `SourceLoader`.
```toml
[dependencies]
decodal = "0.1"
```
## CLI
A standalone CLI is kept in this repository as the `decodal-cli` workspace package.
It builds a `decodal` binary, but it is not the primary crates.io package.
Run a Decodal file from the repository:
```sh
cargo run -q -p decodal-cli -- examples/advanced/main.dcdl
```
Enable optional regex support when needed:
```sh
cargo run -q -p decodal-cli --features regex -- examples/regex/main.dcdl
```
## Web playground
The static documentation site and browser playground live under:
```text
site/decodal-site/
```
## License
Licensed under either of:
- Apache License, Version 2.0
- MIT license
at your option.

View File

@ -9,12 +9,13 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly evaluate: (a: number, b: number, c: number) => void;
readonly evaluateProject: (a: number, b: number, c: number, d: number, e: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_export: (a: number, b: number) => number;
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
readonly evaluate: (a: number, b: number) => [number, number];
readonly evaluateProject: (a: number, b: number, c: number, d: number) => [number, number];
readonly __wbindgen_externrefs: WebAssembly.Table;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
readonly __wbindgen_start: () => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;

View File

@ -8,18 +8,14 @@ export function evaluate(source) {
let deferred2_0;
let deferred2_1;
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_export, wasm.__wbindgen_export2);
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.evaluate(retptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
deferred2_0 = r0;
deferred2_1 = r1;
return getStringFromWasm0(r0, r1);
const ret = wasm.evaluate(ptr0, len0);
deferred2_0 = ret[0];
deferred2_1 = ret[1];
return getStringFromWasm0(ret[0], ret[1]);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
}
}
@ -32,25 +28,30 @@ export function evaluateProject(entry, files_json) {
let deferred3_0;
let deferred3_1;
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_export, wasm.__wbindgen_export2);
const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(files_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
const ptr1 = passStringToWasm0(files_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.evaluateProject(retptr, ptr0, len0, ptr1, len1);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
deferred3_0 = r0;
deferred3_1 = r1;
return getStringFromWasm0(r0, r1);
const ret = wasm.evaluateProject(ptr0, len0, ptr1, len1);
deferred3_0 = ret[0];
deferred3_1 = ret[1];
return getStringFromWasm0(ret[0], ret[1]);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
function __wbg_get_imports() {
const import0 = {
__proto__: null,
__wbindgen_init_externref_table: function() {
const table = wasm.__wbindgen_externrefs;
const offset = table.grow(4);
table.set(0, undefined);
table.set(offset + 0, undefined);
table.set(offset + 1, null);
table.set(offset + 2, true);
table.set(offset + 3, false);
},
};
return {
__proto__: null,
@ -58,14 +59,6 @@ function __wbg_get_imports() {
};
}
let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}
function getStringFromWasm0(ptr, len) {
return decodeText(ptr >>> 0, len);
}
@ -149,8 +142,8 @@ function __wbg_finalize_init(instance, module) {
wasmInstance = instance;
wasm = instance.exports;
wasmModule = module;
cachedDataViewMemory0 = null;
cachedUint8ArrayMemory0 = null;
wasm.__wbindgen_start();
return wasm;
}

View File

@ -1,9 +1,10 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const evaluate: (a: number, b: number, c: number) => void;
export const evaluateProject: (a: number, b: number, c: number, d: number, e: number) => void;
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
export const __wbindgen_export: (a: number, b: number) => number;
export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export3: (a: number, b: number, c: number) => void;
export const evaluate: (a: number, b: number) => [number, number];
export const evaluateProject: (a: number, b: number, c: number, d: number) => [number, number];
export const __wbindgen_externrefs: WebAssembly.Table;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_free: (a: number, b: number, c: number) => void;
export const __wbindgen_start: () => void;

View File

@ -1,7 +1,13 @@
{
"name": "decodal-wasm",
"type": "module",
"description": "WebAssembly wrapper for evaluating Decodal in browser playgrounds.",
"version": "0.1.0",
"license": "MIT OR Apache-2.0",
"repository": {
"type": "git",
"url": "https://gitea.hareworks.net/Hare/Decodal"
},
"files": [
"decodal_wasm_bg.wasm",
"decodal_wasm.js",
@ -11,5 +17,11 @@
"types": "decodal_wasm.d.ts",
"sideEffects": [
"./snippets/*"
],
"keywords": [
"decodal",
"wasm",
"dsl",
"config"
]
}