#ron #serialization #parser

ron2

RON parser with full AST access, perfect round-trip fidelity, and formatting tools

4 releases (2 breaking)

Uses new Rust 2024

0.3.0 Jan 25, 2026
0.2.1 Jan 22, 2026
0.2.0 Jan 21, 2026
0.1.0 Jan 20, 2026

#476 in Encoding


Used in 3 crates

MIT/Apache

635KB
15K SLoC

ron-extras

License MSRV

A RON parser with full AST access, schema-driven LSP completions, and formatting tools.

Quick Start

[dependencies]
ron2 = "0.3"
use ron2::{FromRon, Ron};

#[derive(Ron, Debug)]
struct Config {
    /// Server port
    port: u16,
    /// Optional hostname
    host: Option<String>,
}

fn main() -> ron2::Result<()> {
    let config = Config::from_ron("(port: 8080, host: \"localhost\")")?;
    println!("{:?}", config);
    Ok(())
}

See Derive Attributes for the full attribute reference.

Crates

Crate Description
ron2 Core parser with AST and Value APIs
ron2-derive Derive macros for FromRon, ToRon, and schema generation
ron2-lsp Language server with completions and diagnostics
ron2-doc Documentation generator from schema files
ron2-cli CLI tools (ron fmt, ron doc)

Why ron2?

  • AST-first: Parses to a complete AST preserving all source information
  • Perfect round-trip: Preserves comments, whitespace, and formatting exactly
  • Rich errors: Full source spans enable beautiful error messages (see Error Reporting)
  • Full RON data model: Avoids serde limitations through custom derive

ron2 works best for human-edited config files or building custom DSLs. See the showcase example for a complete demonstration of derive macros, schema validation, and formatting.

Error Reporting

ron2 provides full source spans on all parsed values via the Spanned<T> wrapper. This enables beautiful error messages with libraries like ariadne:

#[derive(FromRon)]
struct ServerConfig {
    host: Spanned<String>,
    port: Spanned<u16>,
    max_connections: Spanned<u32>,
}

// Access value and span separately
if config.port.value == 0 {
    report_error("port cannot be 0", config.port.span);
}
Error: max_connections (10) must be >= min_connections (100)
   ╭─[ config.ron:5:22 ]5 │     max_connections: 10,
   │                      ─┬
   │                       ╰── max_connections (10) must be >= min_connections (100)
   │
   │ Help: increase max_connections or decrease min_connections
───╯

See the error-report example for a complete implementation.

Schema & LSP

ron2 can generate schema files from your Rust types. The LSP uses these schemas for completions and validation.

Generate schemas by calling write_schemas (e.g., from a test or a dedicated binary):

use ron2::schema::RonSchema;

// Write to a specific directory
Config::write_schemas(Some("./schemas"))?;

// Or use the default XDG location (~/.local/share/ron-schemas/)
Config::write_schemas(None)?;

The LSP searches for schemas in this order:

  1. Configured schemaDirs (from editor settings)
  2. RON_SCHEMA_DIR environment variable
  3. XDG default (~/.local/share/ron-schemas/)

Add the #![type] attribute to your RON files to enable completions:

#![type = "my_crate::config::Config"]

(
    port: 8080,
    host: "localhost",
)

CLI Tools

Install the CLI:

cargo install ron2-cli

Format RON files:

ron fmt config.ron              # Format in place
ron fmt --check config.ron      # Check formatting (for CI)

Generate documentation from schemas:

ron doc ./schemas -o ./docs

When Not to Use ron2

Don't use ron2 if:

  • You need serde compatibility (your types only implement Serialize/Deserialize)
  • You're parsing large data volumes where speed matters more than AST fidelity

Alternatives:

  • ron — Mature, serde-based, widely used
  • nanoserde — Minimal dependencies, fast compile times

Editor Setup

See EDITOR_SETUP.md for Helix, VS Code, and other editor integrations.

Acknowledgments & License

Derived from ron. Dual-licensed under Apache-2.0 and MIT.


lib.rs:

RON2 - Rusty Object Notation parser with full AST access

This crate provides a standalone RON parser with three APIs:

  • AST API: Full fidelity parsing with perfect round-trip support
  • Value API: Simplified access to semantic content only
  • Typed Conversions: FromRon and ToRon traits for Rust types

No serde dependency required.

AST Example (full fidelity)

use ron2::ast::{parse_document, serialize_document};

let source = "// config\nPoint(x: 1, y: 2)";
let doc = parse_document(source).unwrap();
let output = serialize_document(&doc).unwrap();
assert_eq!(source, output); // Perfect round-trip

Value Example (semantic only)

use ron2::Value;

let value: Value = "[1, 2, 3]".parse().unwrap();
assert!(matches!(value, Value::Seq(_)));

Typed Conversions

use ron2::{FromRon, ToRon};

let numbers: Vec<i32> = Vec::from_ron("[1, 2, 3]").unwrap();
assert_eq!(numbers, vec![1, 2, 3]);

let ron_string = numbers.to_ron().unwrap();

Dependencies

~1.3–3.5MB
~62K SLoC