Expand description
§yaml-edit
A Rust library for parsing and editing YAML files while preserving formatting, comments, and whitespace. Built with rowan for lossless syntax trees.
§Features
- Lossless parsing - preserves all whitespace, comments, and original formatting
- In-place editing - modify YAML structures while maintaining formatting
- Error recovery - continues parsing even with syntax errors
- Position tracking - detailed error locations for debugging
§Quick Start
use yaml_edit::Document;
use std::str::FromStr;
let doc = Document::from_str("name: old-project\nversion: 1.0.0")?;
if let Some(mapping) = doc.as_mapping() {
mapping.set("name", "new-project");
mapping.set("version", "2.0.0");
}
println!("{}", doc); // Formatting preserved§How It Works
This library uses a persistent syntax tree built on rowan. Understanding this model helps you use the library effectively:
§Lightweight Wrappers
Types like Mapping, Sequence, and Document are lightweight wrappers around syntax tree nodes:
let mapping = doc.as_mapping(); // Just a view into the tree
// mapping is cheap to clone, it's just a reference§In-Place Mutations
Changes are applied directly to the underlying syntax tree using rowan’s splice_children operation:
let mapping = doc.as_mapping().unwrap();
mapping.set("key", "value");
// The change is visible through `doc` immediately
// No need to "put mapping back into doc"§Shared Tree Structure
Multiple wrappers can reference the same underlying tree. When one is mutated, all see the change:
let mapping1 = doc.as_mapping().unwrap();
let mapping2 = doc.as_mapping().unwrap();
mapping2.set("new_key", "new_value");
// mapping1 also sees the change because they reference the same treeThis design enables ergonomic APIs without explicit ownership transfers, efficient mutations without copying, and preserved formatting because edits modify nodes in-place.
§Entry Points
§Document - Single-document YAML (most common)
use yaml_edit::Document;
use std::str::FromStr;
let doc = Document::from_file(&config_path)?;
// Or from a string
let doc = Document::from_str("key: value")?;§YamlFile - Multi-document YAML
use yaml_edit::YamlFile;
use std::str::FromStr;
let yaml = YamlFile::from_str("---\ndoc1: value\n---\ndoc2: value")?;
for doc in yaml.documents() {
// Process each document
}§Mapping / Sequence - Working with collections
let doc = Document::from_str("key: value\nlist:\n - item1\n - item2")?;
if let Some(mapping) = doc.as_mapping() {
mapping.set("new_key", "new_value");
if let Some(list) = mapping.get_sequence("list") {
list.push("item3");
}
}§Common Operations
§Editing mappings
let yaml = r#"
name: my-app
version: 1.0.0
author: Alice
"#;
let doc = Document::from_str(yaml)?;
if let Some(root) = doc.as_mapping() {
root.set("version", "2.0.0");
root.set("license", "MIT");
root.remove("author");
root.rename_key("name", "project_name");
}§Working with sequences
let yaml = "items:\n - one\n - two\n";
let doc = Document::from_str(yaml)?;
if let Some(root) = doc.as_mapping() {
if let Some(items) = root.get_sequence("items") {
items.push("three");
items.set(0, "first"); // Update by index
}
}§Nested modifications
let yaml = r#"
services:
web:
image: nginx:latest
port: 8080
"#;
let doc = Document::from_str(yaml)?;
if let Some(root) = doc.as_mapping() {
if let Some(services) = root.get_mapping("services") {
if let Some(web) = services.get_mapping("web") {
web.set("image", "nginx:alpine");
web.set("port", 80);
}
}
}§Path-based access
use yaml_edit::path::YamlPath;
// Get nested values
let host = doc.get_path("server.host");
// Set nested values (creates intermediate mappings)
doc.set_path("database.credentials.username", "admin");
// Array indexing
doc.get_path("servers[0].host");§Visitor pattern
For traversing and analyzing documents:
use yaml_edit::{Document, visitor::{YamlVisitor, YamlAccept, ScalarCollector}};
use std::str::FromStr;
let doc = Document::from_str("name: my-app\nversion: 1.0.0")?;
let mut collector = ScalarCollector::new();
doc.accept(&mut collector);
// collector.scalars contains all scalar values§Error Handling
use yaml_edit::{Document, YamlError};
use std::str::FromStr;
fn update_config(yaml: &str, new_version: &str) -> Result<String, YamlError> {
let doc = Document::from_str(yaml)?;
let root = doc.as_mapping()
.ok_or_else(|| YamlError::InvalidOperation {
operation: "get root mapping".to_string(),
reason: "Document root is not a mapping".to_string(),
})?;
root.set("version", new_version);
Ok(doc.to_string())
}
§Testing
# Run all tests
cargo test
# Run with all features
cargo test --all-features
# Run YAML Test Suite (requires submodule)
[git](git) submodule update --init
cargo test --test yaml_test_suite§More Examples
See the examples/ directory for more detailed usage.
§License
See LICENSE file for details. A lossless YAML parser and editor.
This library provides a lossless parser for YAML files, preserving all whitespace, comments, and formatting. It is based on the rowan library.
§Mutability Model
Important: This library uses interior mutability through the rowan library.
This means methods taking &self can still modify the underlying syntax tree.
§What This Means
- Types like
Mapping,Sequence, andDocumentcan mutate even from&self - Changes are immediately visible to all holders of the syntax tree
- You don’t need to mark variables as
mutto call mutation methods
§Example
use yaml_edit::Document;
use std::str::FromStr;
let doc = Document::from_str("name: Alice").unwrap(); // Note: not `mut`
let mapping = doc.as_mapping().unwrap(); // Note: not `mut`
// Yet we can still mutate!
mapping.set("age", 30); // This works despite `mapping` not being `mut`
assert_eq!(doc.to_string(), "name: Alice\nage: 30\n");§Why This Design?
This design enables:
- Efficient in-place mutations without cloning the entire tree
- Sharing references while still allowing modifications
- Lossless preservation of formatting and comments during edits
If you’re familiar with RefCell or Rc, this is similar - the tree uses
internal synchronization to allow shared mutable access.
§Migration Note
If you’re coming from other YAML libraries, this might seem unusual. In most
libraries, you need &mut to modify data. Here, you don’t. This is intentional
and allows for a more flexible API while maintaining the guarantees of Rust’s
borrow checker.
§Getting Started
§Parsing YAML
use yaml_edit::Document;
use std::str::FromStr;
let yaml = Document::from_str("name: Alice\nage: 30").unwrap();
let mapping = yaml.as_mapping().unwrap();
// Get values
let name = mapping.get("name").unwrap();
assert_eq!(name.as_scalar().unwrap().to_string(), "Alice");§Modifying YAML
use yaml_edit::Document;
use std::str::FromStr;
let yaml = Document::from_str("name: Alice").unwrap();
let mapping = yaml.as_mapping().unwrap();
// Add a new field
mapping.set("age", 30);
// Update an existing field
mapping.set("name", "Bob");
// Remove a field
mapping.remove("age");§Path-based Access
use yaml_edit::{Document, path::YamlPath};
use std::str::FromStr;
let yaml = Document::from_str("server:\n host: localhost").unwrap();
// Get nested values
let host = yaml.get_path("server.host");
assert!(host.is_some());
// Set nested values (creates intermediate mappings)
yaml.set_path("server.port", 8080);
yaml.set_path("database.host", "db.example.com");§Iterating Over Collections
use yaml_edit::Document;
use std::str::FromStr;
let yaml = Document::from_str("a: 1\nb: 2\nc: 3").unwrap();
let mapping = yaml.as_mapping().unwrap();
// Iterate over key-value pairs
for (key, value) in &mapping {
println!("{:?}: {:?}", key, value);
}
// Use iterator methods
let count = (&mapping).into_iter().count();
assert_eq!(count, 3);§Working with Sequences
use yaml_edit::Document;
use std::str::FromStr;
let yaml = Document::from_str("items:\n - apple\n - banana").unwrap();
let mapping = yaml.as_mapping().unwrap();
let sequence = mapping.get_sequence("items").unwrap();
// Iterate over items
for item in &sequence {
println!("{:?}", item);
}
// Get specific item
let first = sequence.get(0);
assert!(first.is_some());§Schema Validation
use yaml_edit::{Document, SchemaValidator};
use std::str::FromStr;
let yaml = Document::from_str("name: Alice\nage: 30").unwrap();
// Validate against JSON schema (no custom types)
let result = SchemaValidator::json().validate(&yaml);
assert!(result.is_ok());§Position Tracking
use yaml_edit::Document;
use std::str::FromStr;
let text = "name: Alice\nage: 30";
let doc = Document::from_str(text).unwrap();
// Get line/column positions
let start = doc.start_position(text);
assert_eq!(start.line, 1);
assert_eq!(start.column, 1);Re-exports§
pub use custom_tags::CompressedBinaryHandler;pub use custom_tags::CustomTagError;pub use custom_tags::CustomTagHandler;pub use custom_tags::CustomTagParser;pub use custom_tags::CustomTagRegistry;pub use custom_tags::EnvVarHandler;pub use custom_tags::JsonHandler;pub use custom_tags::TimestampHandler;
Modules§
- advanced
- Advanced API for power users who need direct access to the underlying syntax tree.
- anchor_
resolution - Anchor and alias resolution for semantic YAML operations
- custom_
tags - Custom YAML tag system for format-preserving parsing and serialization
- debug
- Debug utilities for inspecting and understanding YAML structures.
- error_
recovery - Error recovery mechanisms for YAML parsing
- path
- Path-based access to YAML documents.
- validator
- YAML specification validator
- visitor
- Visitor pattern for traversing YAML AST nodes.
Structs§
- Alias
- A YAML alias reference (e.g., *anchor_name)
- Custom
Schema - Custom schema definition with user-defined validation rules
- Directive
- A YAML directive like %YAML 1.2
- Document
- A single YAML document
- Line
Column - A line and column position in a YAML document (1-indexed).
- Mapping
- A YAML mapping (key-value pairs)
- Mapping
Builder - Builder for YAML mappings.
- Mapping
Entry - A key-value pair in a YAML mapping
- Parse
- The result of a parse operation.
- Positioned
Parse Error - A positioned parse error containing location information.
- Scalar
- A YAML scalar value
- Scalar
Value - A scalar value with metadata about its style and content
- Schema
Validator - Schema validator for YAML documents
- Sequence
- A YAML sequence (list)
- Sequence
Builder - Builder for YAML sequences.
- Set
- A virtual AST node for YAML sets (!!set tagged scalars)
- Tagged
Node - A YAML tagged scalar (tag + value)
- Text
Position - A text position in a YAML document, represented as byte offsets.
- Validation
Config - Configuration for whitespace and formatting validation
- Validation
Error - Error that occurs during schema validation
- Whitespace
Error - Whitespace and formatting validation errors
- Yaml
Builder - A builder for constructing YAML documents with a fluent API.
- Yaml
File - A YAML file containing one or more documents
Enums§
- Custom
Validation Result - Result of a custom validation function
- Indentation
- The indentation to use when writing a YAML file.
- Lang
- YAML language type for rowan.
- Parse
Error Kind - The kind of parse error, enabling structured matching without string parsing.
- Scalar
Conversion Error - Error type for scalar type conversions
- Scalar
Style - Style of scalar representation in YAML
- Scalar
Type - Type of a scalar value
- Schema
- YAML Schema types as defined in YAML 1.2 specification
- Syntax
Kind - YAML Concrete Syntax Tree (CST) node types.
- Validation
Error Kind - Specific type of validation error that occurred
- Whitespace
Error Category - Categories of whitespace errors
- Yaml
Error - Errors that can occur when working with YAML documents
- Yaml
Kind - The kind of YAML value.
- Yaml
Node - A type-erased handle to a CST node returned from navigation methods.
Traits§
- AsYaml
- Trait for types that can be represented as YAML content.
Functions§
- byte_
offset_ to_ line_ column - Convert a byte offset to line and column numbers in the given text.
- lex
- Tokenize YAML input with whitespace validation
- lex_
with_ validation - Tokenize YAML input with whitespace and formatting validation
- lex_
with_ validation_ config - Tokenize YAML input with custom validation configuration
- yaml_eq
- Compare two
AsYamlvalues for semantic equality.
Type Aliases§
- Validation
Result - Result type for schema validation operations
- Yaml
Result - Result type for yaml-edit operations