ron-schema-validator
Schema validation for RON (Rusty Object Notation) files. Define the expected structure of your .ron data in a .ronschema file, then validate against it — catching type mismatches, missing fields, invalid enum variants, and more.
RON has no equivalent of JSON Schema. This project fills that gap.
Status: v0.9 — Schema parser, RON parser, validator, and CLI are all functional with test coverage. JSON output, default field values, warnings, and schema imports available.
Schema Format
Schemas use a custom .ronschema format that mirrors the shape of the data it validates. Types replace values:
// config.ronschema
(
name: String,
version: Integer,
debug: Bool,
description: Option(String),
tags: [String],
window: (
width: Integer,
height: Integer,
),
)
A matching .ron data file:
// config.ron
(
name: "my-app",
version: 3,
debug: true,
description: Some("A sample application"),
tags: ["cli", "tools"],
window: (
width: 1920,
height: 1080,
),
)
Supported Types
| Type | Matches | Example |
|---|---|---|
String |
Quoted strings | "hello" |
Integer |
Signed integers (i64) | 42, -1 |
Float |
Floating-point values (f64) | 3.14 |
Bool |
Boolean literals | true, false |
Option(T) |
Some(value) or None |
Some(5), None |
[T] |
Homogeneous list | [1, 2, 3] |
{K: V} |
Map with typed keys and values | {"str": 5, "dex": 3} |
(T1, T2, ...) |
Positional tuple | (1.0, 2.5) |
| Inline struct | Nested (...) with named fields |
See example above |
| Enum reference | Bare identifier from a defined enum | Creature, Damage(5) |
| Type alias | Named type via type Name = T |
See below |
Enums
Define enums after the root struct. Variants can be unit (bare identifiers) or carry associated data:
(
status: Status,
effect: Effect,
)
enum Status { Active, Inactive, Pending }
enum Effect { Damage(Integer), Heal(Integer), Draw }
Type Aliases
Define reusable types with type Name = T:
(
cost: Cost,
backup_cost: Cost,
)
type Cost = (generic: Integer, sigil: Integer,)
Default Values
Fields with defaults are optional — they don't produce errors when absent from data:
(
name: String,
label: String = "unnamed",
count: Integer = 0,
tags: [String] = [],
status: Status = Active,
)
enum Status { Active, Inactive }
Default values are type-checked against their field's declared type at schema parse time.
Imports
Share enums and type aliases across schemas using import statements at the top of a file:
// shared-types.ronschema
enum Rarity { Common, Uncommon, Rare }
type Label = String
// item.ronschema
import "shared-types.ronschema"
(
name: String,
rarity: Rarity,
label: Label,
)
Import paths are resolved relative to the importing schema's directory. Circular imports and name collisions between imported and local types are reported as parse errors. Import nesting is limited to 10 levels.
Maps
Map types use {KeyType: ValueType}. Keys must be String, Integer, or an enum type:
(
attributes: {String: Integer},
)
CLI Usage
ron-schema validate --schema config.ronschema target.ron
Pass a directory as the target to validate all .ron files within it:
ron-schema validate --schema card.ronschema cards/
Use --format json for machine-readable output:
ron-schema validate --schema config.ronschema data/ --format json
Use --deny-warnings to treat warnings as errors (exit code 1):
ron-schema validate --schema config.ronschema data/ --deny-warnings
Library Usage
The library crate (ron-schema) operates on &str — no file I/O, no formatting opinions.
use ;
let schema = parse_schema?;
let value = parse_ron?;
let result = validate;
for error in &result.errors
Validation collects all errors rather than failing on the first — useful when batch-validating many files.
Project Structure
ron-schema-validator/
├── ron-schema/ ← library crate (zero external dependencies)
│ └── src/
│ ├── lib.rs ← public API re-exports
│ ├── span.rs ← Position, Span, Spanned<T>
│ ├── error.rs ← error types (schema, RON, validation)
│ ├── diagnostic.rs
│ ├── schema/ ← schema AST + parser
│ └── ron/ ← RON value types + parser
└── ron-schema-cli/ ← binary crate (clap)
└── src/
└── main.rs
Building
cargo build
cargo test
Requires Rust 2021 edition.
Roadmap
MVP (v0.1)
- Workspace and crate scaffolding
- Source location types (
Span,Position,Spanned<T>) - Error types (schema parsing, RON parsing, validation)
- Schema AST types
- RON value types
- Source line extraction for diagnostics
- Schema parser (
.ronschema→ AST) - RON data parser (
.ron→Spanned<RonValue>) - Validation engine
- CLI wiring (file I/O, error rendering, batch mode)
- Test coverage for all error kinds
v0.2 — Type Aliases
-
type Name = Tdefinitions - Recursive alias detection
- Alias resolution during validation
v0.3 — Map Types
-
{K: V}schema syntax -
{ key: value, ... }RON parsing - Map key/value validation
- Key type restriction (String, Integer, enum)
v0.4 — Tuple Types
-
(T1, T2, ...)schema syntax - Tuple parsing in RON data with struct/tuple disambiguation
- Tuple length and element type validation
v0.5 — Enum Variants with Data
-
Variant(Type)schema syntax -
Variant(value)RON parsing - Unit vs data variant validation
v0.6 — JSON Output
-
--format jsonfor machine-readable output - Structured error objects with code, severity, path, message, span
- Schema parse errors surfaced in JSON with
success: false
v0.7 — Default Values
-
field: Type = <value>syntax for optional fields - Fields with defaults not required in data
- Default values type-checked at schema parse time
v0.8 — Warnings
- Warning infrastructure parallel to the error system
-
FieldOrderMismatchwarning when data field order differs from schema -
--deny-warningsflag causes exit code 1 on warnings - Warnings rendered in both human and JSON output formats
v0.9 — Schema Composition / Imports
-
import "path"syntax at the top of schema files - Imported enums and type aliases merged into importing schema
-
SchemaResolvertrait keeps the library filesystem-free - Circular import detection with 10-level nesting cap
- Name collision detection between imports and local types
Future
- Custom validation rules (value ranges, string patterns)
-
initsubcommand (schema inference)
Design Decisions
| Decision | Rationale |
|---|---|
Custom .ronschema format |
Mirrors data shape, reads like Rust type definitions. More readable than embedding schema rules in RON. |
| Custom RON parser | The ron crate's Value type discards bare identifier names, making enum validation impossible. |
| All fields required by default | Mirrors Rust semantics. Option(T) controls the value, not whether the field can be absent. |
| Collect all errors | Primary use case is batch-validating many files. Users need all problems at once. |
| Zero library dependencies | The library crate has no external dependencies. |
License
MIT