`yaml-rust` is a pure Rust library designed for parsing and emitting YAML 1.2 documents. Unlike `serde_yaml`, which focuses on direct serialization and deserialization between Rust structs and YAML, `yaml-rust` provides a more low-level, Document Object Model (DOM)-like representation of YAML data. This makes it particularly useful when you need to work with YAML configurations where the structure might be dynamic, not fully known at compile time, or when you require fine-grained control over individual YAML nodes.
Key Features:
* Parsing: It can parse YAML strings or readers into a vector of `yaml_rust::Yaml` enums. Each `Yaml` enum represents a YAML document or a node within a document (e.g., Scalar, Array, Hash, Null, Boolean, Integer, Float).
* Emitting: Conversely, it can take a `yaml_rust::Yaml` enum structure and format it back into a YAML string or writer.
* DOM-like Access: The `Yaml` enum allows for easy navigation and manipulation of the YAML structure using methods like `as_hash()`, `as_vec()`, `as_str()`, etc., and standard Rust collection indexing (e.g., `doc[0]["key"]`).
* Flexibility: Because it doesn't require predefined Rust structs, `yaml-rust` offers great flexibility for handling varied or evolving YAML schemas. You can read, inspect, modify, and write YAML data without strict type mappings.
* Pure Rust: Implemented entirely in Rust, it integrates seamlessly into Rust projects without external C dependencies.
When to use `yaml-rust`:
* When you need to dynamically read and write YAML configurations where the structure is not fixed or known in advance.
* For tasks like schema validation, merging YAML files, or making programmatic changes to YAML documents without necessarily mapping them to Rust structs.
* When `serde_yaml`'s strong typing is too restrictive for your use case.
Getting Started:
To use `yaml-rust` in your project, add it to your `Cargo.toml` dependencies:
```toml
[dependencies]
yaml-rust = "0.4"
```
The library provides the `YamlLoader` for parsing and `YamlEmitter` for emitting, along with the central `Yaml` enum for data representation.
Example Code
use yaml_rust::{YamlLoader, YamlEmitter, Yaml};
use std::collections::BTreeMap; // For creating a Hash/Map
fn main() -> Result<(), Box<dyn std::error::Error>> {
// --- 1. Define a sample YAML string ---
let yaml_str = "
---
# Document 1: Application Configuration
application:
name: MyAwesomeApp
version: 1.0.0
debug_mode: true
database:
host: localhost
port: 5432
users:
- name: admin
role: superuser
- name: guest
role: readonly
settings: { timeout: 3000, max_connections: 100 } # Inline map
---
# Document 2: User List
- user_id: 101
username: alice
active: true
- user_id: 102
username: bob
active: false
";
println!("--- Original YAML ---\n{}", yaml_str);
// --- 2. Parse the YAML string ---
let docs = YamlLoader::load_from_str(yaml_str)?;
let doc1 = &docs[0]; // Access the first document
let doc2 = &docs[1]; // Access the second document
println!("\n--- Parsed Data (Document 1) ---");
// Accessing elements from Document 1 (application configuration)
if let Some(app_name) = doc1["application"]["name"].as_str() {
println!("Application Name: {}", app_name);
}
if let Some(db_port) = doc1["application"]["database"]["port"].as_i64() {
println!("Database Port: {}", db_port);
}
if let Some(users) = doc1["application"]["database"]["users"].as_vec() {
println!("Database Users:");
for user_node in users {
if let (Some(name), Some(role)) = (user_node["name"].as_str(), user_node["role"].as_str()) {
println!(" - Name: {}, Role: {}", name, role);
}
}
}
if let Some(timeout) = doc1["application"]["settings"]["timeout"].as_i64() {
println!("Setting Timeout: {}", timeout);
}
println!("\n--- Parsed Data (Document 2) ---");
// Accessing elements from Document 2 (user list)
if let Some(users_list) = doc2.as_vec() {
for user_data in users_list {
if let (Some(id), Some(username), Some(active)) = (
user_data["user_id"].as_i64(),
user_data["username"].as_str(),
user_data["active"].as_bool(),
) {
println!(" User ID: {}, Username: {}, Active: {}", id, username, active);
}
}
}
// --- 3. Modify a Yaml object ---
let mut modified_doc1 = doc1.clone(); // Clone to modify
if let Some(app_hash) = modified_doc1["application"].as_hash_mut() {
// Change existing value
app_hash.insert(
Yaml::from_str("version"),
Yaml::from_str("1.1.0-BETA"),
);
// Add a new key-value pair
app_hash.insert(
Yaml::from_str("environment"),
Yaml::from_str("development"),
);
}
// Also modify the first user in the database list
if let Some(users_vec) = modified_doc1["application"]["database"]["users"].as_vec_mut() {
if let Some(first_user) = users_vec.get_mut(0) {
if let Some(user_hash) = first_user.as_hash_mut() {
user_hash.insert(Yaml::from_str("status"), Yaml::from_str("active"));
}
}
}
// --- 4. Emit the modified Yaml object back to a string ---
let mut emitter = String::new();
let mut yam_emitter = YamlEmitter::new(&mut emitter);
yam_emitter.dump(&modified_doc1)?; // Dump the first (modified) document
yam_emitter.dump(&doc2)?; // Dump the second (unmodified) document
println!("\n--- Modified and Emitted YAML ---");
println!("{}", emitter);
// --- 5. Creating a YAML from scratch and emitting it ---
println!("\n--- Dynamically Created YAML ---");
let mut new_doc = BTreeMap::new();
new_doc.insert(Yaml::from_str("title"), Yaml::from_str("New Config"));
new_doc.insert(Yaml::from_str("enabled"), Yaml::Boolean(true));
new_doc.insert(Yaml::from_str("count"), Yaml::Integer(123));
let mut nested_list = Vec::new();
nested_list.push(Yaml::String("item1".to_string()));
nested_list.push(Yaml::String("item2".to_string()));
new_doc.insert(Yaml::from_str("items"), Yaml::Array(nested_list));
let final_yaml = Yaml::Hash(new_doc);
let mut new_emitter = String::new();
let mut yam_emitter_new = YamlEmitter::new(&mut new_emitter);
yam_emitter_new.dump(&final_yaml)?;
println!("{}", new_emitter);
Ok(())
}








yaml-rust