Derive serde’s Serialize and call oneyaml::to_string. Fields come out in declaration order, in block style.
cargo new write-yaml && cd write-yaml cargo add [email protected] serde -F serde/derive
Put this in src/main.rs and run cargo run:
use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct Service { name: String, image: String, replica_count: u32, ports: Vec<u16>, env: BTreeMap<String, String>, #[serde(skip_serializing_if = "Option::is_none")] healthcheck: Option<String>, note: String, } fn main() -> Result<(), Box<dyn std::error::Error>> { let svc = Service { name: "web".into(), image: "nginx:1.27".into(), replica_count: 2, ports: vec![80, 443], env: BTreeMap::from([("LOG_LEVEL".into(), "info".into())]), healthcheck: None, note: "no".into(), }; let yaml = oneyaml::to_string(&svc)?; std::fs::write("service.yaml", &yaml)?; print!("{yaml}"); // Read it back: the same value. let back: Service = oneyaml::from_str(&std::fs::read_to_string("service.yaml")?)?; println!("round trip equal: {}", back == svc); Ok(()) }
name: web image: nginx:1.27 replicaCount: 2 ports: - 80 - 443 env: LOG_LEVEL: info note: 'no' round trip equal: true
healthcheck is None and skip_serializing_if leaves it out; without it, it’s written as null. to_writer writes to any io::Write, and to_string_with takes SerializeSettings.
Limits
- No comments. A struct has nowhere to keep them, so the output has none. To change a file people maintain by hand and keep its comments, edit it as a
Document: edit YAML and keep its comments. - Strings stay strings for every reader. A string that another reader could take for a different type is quoted:
noteis written'no', so YAML 1.1 tools don’t read it asfalse(the Norway problem).
To read the file into the struct: parse YAML into a struct.