hedl
The unified HEDL API—parse, validate, convert, and canonicalize with a single, ergonomic interface.
Production systems need simple APIs. Importing a dozen crates for basic operations creates cognitive overhead. Feature flags should work intuitively. Errors need rich context without boilerplate. The most common operations should be frictionless.
hedl is the facade crate providing the complete HEDL experience through 7 core functions and comprehensive error handling utilities. Zero default dependencies—only pay for features you enable. Full re-exports from 13 specialized crates beneath a unified API. Designed for developers who want results, not dependency management.
What's Implemented
Unified API with zero-overhead ergonomics:
- 7 Core Functions: parse, parse_lenient, canonicalize, to_json, from_json, lint, validate
- 8 Feature Flags: serde, yaml, xml, csv, parquet, neo4j, toon, all-formats
- Zero Default Dependencies: Core parsing only—enable formats as needed
- Error Context Extensions: HedlResultExt trait with .context() and .with_context()
- Full Re-Exports: 13 internal crates exposed through unified namespace
- Type Aliases: Convenient Result = std::result::Result<T, HedlError>
- 130+ Error Extension Tests: Comprehensive validation of context propagation
- Minimal Overhead: < 1% compared to using crates directly
- Feature Composition: Mix and match formats without conflicts
- Ergonomic Imports: use hedl::*; gives you everything you need
Installation
Minimal (Core Only)
Parse, validate, and work with HEDL documents:
[]
= "1.2"
What you get: Parsing, canonicalization, linting, validation What you don't get: Format conversion (JSON/YAML/XML/etc.)
With Specific Formats
Enable only the formats you need:
[]
= { = "1.2", = ["yaml", "xml"] }
All Features
Get everything (recommended for applications):
[]
= { = "1.2", = ["all-formats"] }
Includes: JSON, YAML, XML, CSV, Parquet, Neo4j, TOON, Serde
Core API
parse(bytes: &[u8]) -> Result
Parse HEDL document with strict validation:
use parse;
let hedl = br#"
%VERSION: 1.0
%STRUCT: User: [id, name, age]
---
users: @User
| alice, Alice Smith, 30
| bob, Bob Jones, 25
"#;
let doc = parse?;
println!;
println!;
Behavior:
- Strict parsing (all errors fatal)
- Schema validation
- Reference checking
- UTF-8 validation
Use When: Production parsing, CI/CD validation, strict correctness required
parse_lenient(bytes: &[u8]) -> Result
Parse with relaxed validation:
let doc = parse_lenient?;
// Tolerates some malformations:
// - Missing schemas (inferred from data)
// - Unresolved references (preserved as strings)
// - Inconsistent indentation (best-effort parse)
Use When: Parsing user-generated content, migration from other formats, exploratory analysis
validate(bytes: &[u8]) -> Result<()>
Validate document without constructing AST:
use validate;
if validate.is_ok
Performance: Faster than parse() when you only need validation (no AST construction)
Use Cases: CI/CD checks, pre-commit hooks, batch validation
canonicalize(doc: &Document) -> Result
Convert to canonical form with ditto optimization:
use ;
let doc = parse?;
let canonical = canonicalize?;
// Same document always produces identical output
// Perfect for version control and content-addressable storage
Output Characteristics:
- Deterministic formatting (2-space indentation)
- Ditto operator for repeated values
- Count hints on matrix lists
- Normalized floats (no trailing zeros)
- Sorted keys (optional)
Use When: Git commits, content hashing, cache keys, comparing documents
lint(doc: &Document) -> Vec
Run lint checks for best practices:
use ;
let doc = parse?;
let diagnostics = lint;
for diag in diagnostics
Rules Checked (default):
- id-naming: ID naming conventions
- unused-schema: Unused %STRUCT definitions
- empty-list: Empty matrix lists
- unqualified-kv-ref: Unqualified references in key-value context
Use When: Code review automation, CI/CD quality gates, pre-commit hooks
to_json(doc: &Document) -> Result
Convert to JSON (requires "json" feature, enabled by default):
use ;
let doc = parse?;
let json = to_json?;
// Use with existing JSON APIs
Configuration: Use hedl_json::to_json_with_config() for custom options (pretty-printing, type preservation)
from_json(json: &str) -> Result
Convert from JSON to HEDL:
use from_json;
let json = r#"{"users": [{"id": "alice", "name": "Alice"}]}"#;
let doc = from_json?;
// Now use HEDL's structured API
Type Inference: Automatically detects arrays-of-objects → matrix lists
Error Handling with Context
The Problem
Standard Rust errors lose context during propagation:
The Solution: HedlResultExt
Add context at error boundaries:
use ;
Output:
Failed to parse config: config.hedl
caused by: Parse error at line 42: unexpected token ':'
HedlResultExt Methods
.context(msg: &str)
Add static context:
parse.context?;
.with_context<F: FnOnce() -> String>(f: F)
Add computed context (lazily evaluated):
parse
.with_context?;
Benefits:
- Lazy: Closure only evaluated on error (zero cost on success path)
- Flexible: Capture variables, format strings, complex logic
.map_err_to_hedl()
Convert external errors to HedlError:
read
.map_err_to_hedl
.context?;
Use Cases:
- Wrapping std::io::Error
- Converting serde errors
- Unifying error types
Feature Flags
serde
Enable Serde serialization for Document type:
= { = "1.2", = ["serde"] }
Provides: Serialize/Deserialize impls for Document, Value, Reference
Use Cases: Storing parsed documents in databases, IPC, caching
yaml
Enable YAML format conversion:
= { = "1.2", = ["yaml"] }
Adds:
hedl::to_yaml(doc) -> Result<String>hedl::from_yaml(yaml) -> Result<Document>
Dependencies: serde_yaml 0.9
xml
Enable XML format conversion:
= { = "1.2", = ["xml"] }
Adds:
hedl::to_xml(doc) -> Result<String>hedl::from_xml(xml) -> Result<Document>
Dependencies: quick-xml 0.31
csv
Enable CSV format conversion:
= { = "1.2", = ["csv"] }
Adds:
hedl::to_csv(doc) -> Result<String>(exports first entity list)hedl::from_csv(csv, type_name) -> Result<Document>
Dependencies: csv 1.3
parquet
Enable Apache Parquet columnar format:
= { = "1.2", = ["parquet"] }
Adds:
hedl::to_parquet(doc, writer) -> Result<()>hedl::from_parquet(reader) -> Result<Document>
Dependencies: arrow 57.0, parquet 57.0
neo4j
Enable Neo4j Cypher generation:
= { = "1.2", = ["neo4j"] }
Adds:
hedl::to_cypher(doc) -> Result<String>- Cypher CREATE/MERGE statements for Neo4j import
Dependencies: None (pure Cypher generation)
toon
Enable TOON format (token-optimized for LLMs):
= { = "1.2", = ["toon"] }
Adds:
hedl::to_toon(doc) -> Result<String>hedl::from_toon(toon) -> Result<Document>
Use Case: Maximum token efficiency for LLM contexts
all-formats
Enable all format converters:
= { = "1.2", = ["all-formats"] }
Equivalent to: ["serde", "yaml", "xml", "csv", "parquet", "neo4j", "toon"]
Recommended for: Applications (not libraries)
Re-Exported Modules
Full access to specialized crates through unified namespace:
use ;
Use When: Need advanced configuration beyond core API (e.g., hedl::json::JsonConfig for custom JSON options)
Common Patterns
Parse and Convert
use ;
let doc = parse?;
let json = to_json?;
// Send JSON to REST API
Validate Before Processing
use ;
if validate.is_err
let doc = parse?; // Safe to unwrap
Multi-Format Pipeline
use ;
let doc = from_json?;
let yaml = to_yaml?;
let xml = to_xml?;
Error Context Chaining
use ;
Type Aliases
Convenient Result type with HedlError:
pub type Result<T> = Result;
// Use in function signatures
Performance Characteristics
Zero Overhead: Facade adds < 1% overhead vs using crates directly
Feature Gating: Unused features don't bloat binary (tree-shaking works)
Compile Time: Minimal—most work delegated to specialized crates
Binary Size: Core-only build ~200 KB, all-formats ~800 KB (compressed)
Use Cases
Application Development: Use features = ["all-formats"] for maximum flexibility without managing individual crate versions.
Library Development: Minimize dependencies—only enable formats your library needs. Let downstream users add more formats via their own dependencies.
CLI Tools: Import hedl with all-formats for comprehensive format support in user-facing tools.
Embedded Systems: Use core-only (no features) for minimal binary size. Add formats selectively based on constraints.
Microservices: Enable only formats you consume/produce (e.g., JSON for REST APIs, Parquet for analytics export).
What This Crate Doesn't Do
Streaming Parsing: Use hedl-stream directly for memory-efficient streaming of multi-GB files.
LSP/MCP Servers: Use hedl-lsp or hedl-mcp directly for server applications.
C/WASM Bindings: Use hedl-ffi or hedl-wasm directly for non-Rust integrations.
Benchmarking: Use hedl-bench directly for performance measurement and regression detection.
Testing Utilities: Use hedl-test directly for shared test fixtures and builders.
Dependencies
Core (Zero Features)
hedl-core1.0 - Parsing and ASThedl-c14n1.0 - Canonicalizationhedl-json1.0 - JSON (always included)hedl-lint1.0 - Lintingthiserror1.0 - Error types
Optional (Feature-Gated)
hedl-yaml1.0 - YAML conversion (feature: yaml)hedl-xml1.0 - XML conversion (feature: xml)hedl-csv1.0 - CSV conversion (feature: csv)hedl-parquet1.0 - Parquet conversion (feature: parquet)hedl-neo4j1.0 - Neo4j Cypher (feature: neo4j)hedl-toon1.0 - TOON format (feature: toon)serde1.0 - Serialization (feature: serde)
License
Apache-2.0