Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert toml-rs result to std::collections::HashMap

Tags:

rust

I'm new to Rust and trying to build something simple to get going. I want to load data from a .toml file and use rustache to render out some text from it.

Rustache appears to take a HashMap as its data source, and I'm sure from looking at the toml-rs docs that I should be able to convert its Table and Array types to HashMaps and Vecs, and I suspect it's got something to do with Decoder, but I can't figure it out.

If somebody could provide a short example of how to do this I would be very grateful.

like image 303
Mark Rendle Avatar asked Oct 07 '15 10:10

Mark Rendle


2 Answers

If your data structure has fixed known depth, then all you need is just to pass a correct type to toml::decode():

let value: toml::Value = toml::Value::Table(Parser::new(input).parse().unwrap());
let data: HashMap<String, Vec<u32>> = toml::decode(value).unwrap();

The code above would parse a document like

x = [1, 2, 3]
y = [4, 5, 6]

However, as far as I can see, rustache provides some kind of builder structure which supports arbitrary nesting. In that case you would need to "apply" toml::Value to rustache::HashBuilder. You don't need to use Decodable for this (though you probably can, with some newtypes) - you just need to write a couple of simple functions:

fn toml_into_hashbuilder<'a>(value: toml::Table, mut hb: rustache::HashBuilder<'a>) -> rustache::HashBuilder<'a> {
    for (k, v) in value {
        match v {
            toml::Value::String(s) => hb.insert_string(k, s),
            toml::Value::Integer(i) => hb.insert_int(k, i),
            toml::Value::Float(f) => hb.insert_float(k, f),
            toml::Value::Boolean(b) => hb.insert_bool(k, b),
            toml::Value::Datetime(s) => hb.insert_string(k, s),
            toml::Value::Array(arr) => hb.insert_vector(k, |vb| toml_into_vecbuilder(arr.clone(), vb)),
            toml::Value::Table(tbl) => hb.insert_hash(k, |hb| toml_into_hashbuilder(tbl.clone(), hb))
        }
    }
    hb
}

fn toml_into_vecbuilder<'a>(value: toml::Array, mut vb: rustache::VecBuilder<'a>) -> rustache::VecBuilder<'a> {
    for v in value {
        match v {
            toml::Value::String(s) => vb.push_string(s),
            toml::Value::Integer(i) => vb.push_int(i),
            toml::Value::Float(f) => vb.push_float(f),
            toml::Value::Boolean(b) => vb.push_bool(b),
            toml::Value::Datetime(s) => vb.push_string(s),
            toml::Value::Array(arr) => vb.push_vector(|vb| toml_into_vecbuilder(arr.clone(), vb)),
            toml::Value::Table(tbl) => vb.push_hash(|hb| toml_into_hashbuilder(tbl.clone(), hb))
        }
    }
    vb
}

let value: toml::Table = Parser::new(input).parse().unwrap();
let hb = toml_into_hashbuilder(value, rustache::HashBuilder::new());
let result = rustache::render_text(your_template, hb);

There are unfortunate clones when handling nested tables and arrays - this is a consequence of an issue in rustache. If it is fixed, clone() could be removed, and the closures should be made move then.

like image 136
Vladimir Matveev Avatar answered Nov 12 '22 06:11

Vladimir Matveev


toml::decode is removed since toml 0.3. The new way is using toml::from_str

let data: HashMap<String, Vec<u32>> = toml::from_str(input).unwrap();
like image 1
youknowone Avatar answered Nov 12 '22 06:11

youknowone