I am totally new to Rust, this is the first time I am actually trying to achieve something with it even though I 've read some bit there and there. I know at some time I will have to dive into the book but I would like to solve this simple problem first.
My use case is simple: I would like to parse a YAML file and update a specific field value.
I am using the yaml_rust crate for that purpose. So far I was able to parse the content, turn it into a LinkedHashMap<yaml_rust::Yaml>.
Now I am wondering what would be an efficient way to iterate over this collection to update the field value I am looking for (this is a 3rd level deep nested field) ? I am mainly wondering two things:
Am I using this crate the way it's supposed to be ?
How can I iterate over nested fields ?
This is where I am so far (sorry if that looks quite ugly):
extern crate yaml_rust;
use yaml_rust::{YamlLoader};
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut f = File::open("./template.yaml").expect("Error while opening file");
let mut contents = String::new();
f.read_to_string(&mut contents).expect("Something went wrong while reading the file");
let docs = YamlLoader::load_from_str(&mut contents).unwrap();
let doc = docs[0].as_hash().unwrap();
let iter = doc.iter();
for item in iter {
println!("{:?}", &item);
}
}
To iterate over nested fields, since the map can contain any type of values, you need to match or use the as_* methods on every level. It seems like there are no mutable versions of the latter, so if you need mutable iterators you have to use matching:
match item {
Yaml::Hash(ref mut map) => { // iterate and/or modify the map
}
}
Thank you for your reply, indeed std::fs::read_to_string was a nice replacement
I am trying to use the match you provided, is it supposed to be a replacement for the for in loop or should it be placed inside ? As of now if i put it inside, the compiler throw an error about expecting a tuple when it found an enum (yaml_rust::Yaml).
I was only generally outlining how to get at the sub-maps contained in Yaml values. In the for loop, the individual items are probably (key, value) pairs.
Ah yeah sorry my bad, I got something that kind of works now:
for item in iter {
// println!("{:?}", &item.0);
match item.1 {
Yaml::Hash(ref map) => { // iterate and/or modify the map
println!("{:?}", map);
},
_ => ()
}
}
However I am facing a recursive situation there and I am not sure what's the elegant way to deal with it in Rust. As the map's value is another hash in which I must iterate over and search until I find the Key name I am searching for.
let mut docs = YamlLoader::load_from_str(...).unwrap();
visit_yaml(&mut docs[0], &Yaml::String("CodeUri".into()));
fn visit_yaml(yaml: &mut Yaml, looking_for: &Yaml) -> bool {
if let Yaml::Hash(hash) = yaml {
visit_hash(hash, looking_for)
} else {
false
}
}
fn visit_hash(hash: &mut Hash, looking_for: &Yaml) -> bool {
if let Some(val) = hash.get_mut(looking_for) {
*val = Yaml::String("0ver".into());
return true;
};
for (_, yaml) in hash {
if visit_yaml(yaml, looking_for) {
return true;
}
}
false
}
But since it sounds like you know the exact schema/structure a priori, I think you can just drill down by finding the next level's Yaml::Hash via the same get_mut() approach as the above walk.