Recursive AI

Edit YAML and keep its comments

All oneyaml Rust recipes

Change a YAML file from Rust the way a person would: one value, one list item, one new key, and every comment, blank line and quote elsewhere stays as it was. Parse it into a Document, edit, and write it back.

RUST
use oneyaml::Document;
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let src = "\
# App settings, edited by hand and by tools.
server:
  host: 0.0.0.0
  port: 8080      # behind the load balancer
 
# Feature flags
features:
  - auth
  - legacy-export   # remove after the 2.0 release
  - metrics
";
    let mut doc = Document::parse(src)?;
 
    // Change one value: its trailing comment stays.
    doc.set_path("server.port", 9090)?;
 
    // Remove a list item: its comment goes with it.
    let removed = doc.get_mut("features")?.remove_at(1)?;
    println!("removed {:?} with {:?}", removed.text(), removed.comments());
 
    // Add a key with a comment of its own.
    let mut server = doc.get_mut("server")?;
    let mut timeout = server.insert("timeout", "30s")?;
    timeout.set_trailing(Some("per request"))?;
 
    print!("{doc}");
    Ok(())
}
Text
removed "legacy-export   # remove after the 2.0 release" with ["# remove after the 2.0 release"]
# App settings, edited by hand and by tools.
server:
  host: 0.0.0.0
  port: 9090      # behind the load balancer
  timeout: 30s # per request
 
# Feature flags
features:
  - auth
  - metrics

Good to know

  • Comments stay with their entry. The # behind the load balancer comment stays on port, and the comment of the removed item goes with it. Which entry owns a comment is decided by numbered rules; doc.explain_comment(id) says which rule applied.
  • A removed entry isn’t lost. remove_at returns a Removed with the entry’s text and comments, which you can put back elsewhere with insert_removed. remove_keep_comments removes an entry but leaves its comments in place.
  • Values keep their style. set_path and NodeMut::set keep the old value’s quotes, block style or tag when they still fit the new value.
  • Comments of your own. set_trailing writes the comment after a value, set_leading the lines above it, and Document::set_header the comment at the top of the file. set_blank_lines_before adds spacing.
  • An untouched document writes back byte for byte: Document::parse(src)?.to_string() == src.

To change many values at once from a JSON document, use a patch: merge or patch YAML.

The same edit in Go, Rust and C#: Edit YAML without losing comments.