Rust Logoyaml-rust

`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(())
}