nodes/
toml.rs

1extern crate toml;
2
3pub use self::toml::*;
4
5use std::path::Path;
6use std::fs::File;
7use std::io;
8use std::io::prelude::*;
9
10#[derive(Debug)]
11pub enum LoadError {
12    Open(io::Error),
13    Read(io::Error),
14    Parse(de::Error)
15}
16
17pub trait ValueImpl {
18    fn new() -> Self;
19    fn load<P: AsRef<Path>>(p: P) -> Result<Value, LoadError>;
20    fn save<P: AsRef<Path>>(&self, p: P) -> io::Result<()>;
21    fn find(&self, name: &str) -> Option<&Value>;
22    fn find_mut(&mut self, name: &str) -> Option<&mut Value>;
23    fn set<V: Into<Value>>(&mut self, name: &str, v: V) -> bool;
24}
25
26impl ValueImpl for Value {
27    /// Creates a new empty toml table value.
28    fn new() -> Value {
29        Value::from(value::Table::new())
30    }
31
32    /// Tries to reads and parse the given file.
33    fn load<P: AsRef<Path>>(p: P) -> Result<Value, LoadError> {
34        let mut f = match File::open(p) {
35            Ok(f) => f,
36            Err(e) => return Err(LoadError::Open(e)),
37        };
38
39        let mut s = String::new();
40        if let Err(e) = f.read_to_string(&mut s) {
41            return Err(LoadError::Read(e));
42        }
43
44        match s.parse::<Value>() {
45            Ok(s) => Ok(s),
46            Err(e) => Err(LoadError::Parse(e)),
47        }
48    }
49
50    fn save<P: AsRef<Path>>(&self, p: P) -> io::Result<()> {
51        // this should not fail since we cannot represent invalid
52        // toml values with Value
53        let s = toml::ser::to_string_pretty(&self)
54            .expect("Failed to transform toml to string");
55        File::create(p)?.write_all(s.as_bytes())
56    }
57
58    fn find(&self, name: &str) -> Option<&Value> {
59        toml_find(self, name)
60    }
61
62    fn find_mut(&mut self, name: &str) -> Option<&mut Value> {
63        toml_find_mut(self, name)
64    }
65
66    fn set<V: Into<Value>>(&mut self, name: &str, v: V) -> bool {
67        toml_set(self, name, v.into())
68    }
69}
70
71// TODO: allow array access in name. Like foo.bar.arr:3
72
73/// Returns the value with the given name, if existent.
74/// Can access sub tables, like foo.bar.val
75pub fn toml_find<'a>(v: &'a Value, name: &str)
76        -> Option<&'a Value> {
77    let mut next = v;
78    for part in name.split('.') {
79        let cur = next;
80        match *cur {
81            Value::Table(ref table) =>
82                match table.get(part) {
83                    Some(entry) => next = entry,
84                    None => return None,
85            }, _ => return None,
86        };
87    }
88    Some(next)
89}
90
91pub fn toml_find_mut<'a>(v: &'a mut Value, name: &str)
92        -> Option<&'a mut Value> {
93    let mut next = v;
94    for part in name.split('.') {
95        let cur = next;
96        match *cur {
97            Value::Table(ref mut table) =>
98                match table.get_mut(part) {
99                    Some(entry) => next = entry,
100                    None => return None,
101            }, _ => return None,
102        };
103    }
104    Some(next)
105}
106
107/// Returns false if name cannot be inserted.
108/// E.g. when name is "foo.bar" but "foo" is already a (non-table)
109/// value.
110pub fn toml_set(v: &mut Value, name: &str, val: Value) -> bool {
111    let mut next = v;
112    let mut it = name.split('.');
113    let last = it.next_back().expect("Invalid name given");
114
115    // make sure all sub tables exist, create them if needed
116    for part in it {
117        let cur = next;
118        match *cur {
119            Value::Table(ref mut table) => {
120                let e = table.entry(part.to_string());
121                next = e.or_insert(Value::new());
122            },
123            _ => return false,
124        };
125    }
126
127    // insert
128    match *next {
129        Value::Table(ref mut table) =>
130            table.insert(last.to_string(), val),
131        _ => return false,
132    };
133
134    true
135}