2014-06-20 19:30:08 -05:00
|
|
|
//! A TOML-parsing library
|
|
|
|
//!
|
|
|
|
//! This library is an implementation in Rust of a parser for TOML configuration
|
|
|
|
//! files [1]. It is focused around high quality errors including specific spans
|
|
|
|
//! and detailed error messages when things go wrong.
|
|
|
|
//!
|
|
|
|
//! This implementation currently passes the language agnostic [test suite][2].
|
|
|
|
//!
|
|
|
|
//! # Example
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! let toml = r#"
|
|
|
|
//! [test]
|
|
|
|
//! foo = "bar"
|
|
|
|
//! "#;
|
|
|
|
//!
|
|
|
|
//! let value = toml::Parser::new(toml).parse().unwrap();
|
|
|
|
//! println!("{}", value);
|
|
|
|
//! ```
|
|
|
|
//!
|
2014-06-22 01:35:49 -05:00
|
|
|
//! # Conversions
|
|
|
|
//!
|
|
|
|
//! This library also supports using the standard `Encodable` and `Decodable`
|
|
|
|
//! traits with TOML values. This library provides the following conversion
|
|
|
|
//! capabilities:
|
|
|
|
//!
|
|
|
|
//! * `String` => `toml::Value` - via `Parser`
|
|
|
|
//! * `toml::Value` => `String` - via `Show`
|
|
|
|
//! * `toml::Value` => rust object - via `Decoder`
|
|
|
|
//! * rust object => `toml::Value` - via `Encoder`
|
|
|
|
//!
|
|
|
|
//! Convenience functions for performing multiple conversions at a time are also
|
|
|
|
//! provided.
|
|
|
|
//!
|
2014-06-20 19:30:08 -05:00
|
|
|
//! [1]: https://github.com/mojombo/toml
|
|
|
|
//! [2]: https://github.com/BurntSushi/toml-test
|
2014-06-25 04:22:11 -05:00
|
|
|
//!
|
2014-06-20 19:30:08 -05:00
|
|
|
|
2014-06-20 19:01:38 -05:00
|
|
|
#![crate_type = "lib"]
|
|
|
|
#![feature(macro_rules)]
|
2014-06-20 19:30:08 -05:00
|
|
|
#![deny(warnings, missing_doc)]
|
2014-06-22 01:35:49 -05:00
|
|
|
#![allow(visible_private_types)]
|
|
|
|
|
|
|
|
extern crate serialize;
|
2014-06-20 19:01:38 -05:00
|
|
|
|
2014-07-15 19:30:00 -05:00
|
|
|
use std::collections::TreeMap;
|
2014-06-22 01:35:49 -05:00
|
|
|
use std::from_str::FromStr;
|
2014-06-20 19:01:38 -05:00
|
|
|
|
|
|
|
pub use parser::{Parser, Error};
|
2014-06-22 01:35:49 -05:00
|
|
|
pub use serialization::{Encoder, encode, encode_str};
|
2014-06-23 00:31:09 -05:00
|
|
|
pub use serialization::{Decoder, decode, decode_str};
|
2014-06-22 01:35:49 -05:00
|
|
|
pub use serialization::{Error, NeedsKey, NoValue};
|
|
|
|
pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};
|
2014-06-20 19:01:38 -05:00
|
|
|
|
|
|
|
mod parser;
|
2014-06-22 01:35:49 -05:00
|
|
|
mod show;
|
|
|
|
mod serialization;
|
2014-06-27 09:28:55 -05:00
|
|
|
#[cfg(test)]mod test;
|
2014-06-20 19:30:08 -05:00
|
|
|
/// Representation of a TOML value.
|
2014-06-22 01:35:49 -05:00
|
|
|
#[deriving(PartialEq, Clone)]
|
2014-06-20 19:30:08 -05:00
|
|
|
#[allow(missing_doc)]
|
2014-06-20 19:01:38 -05:00
|
|
|
pub enum Value {
|
|
|
|
String(String),
|
|
|
|
Integer(i64),
|
|
|
|
Float(f64),
|
|
|
|
Boolean(bool),
|
|
|
|
Datetime(String),
|
|
|
|
Array(Array),
|
|
|
|
Table(Table),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type Array = Vec<Value>;
|
2014-07-15 19:30:00 -05:00
|
|
|
pub type Table = TreeMap<String, Value>;
|
2014-06-20 19:01:38 -05:00
|
|
|
|
|
|
|
impl Value {
|
2014-06-20 19:30:08 -05:00
|
|
|
/// Tests whether this and another value have the same type.
|
|
|
|
pub fn same_type(&self, other: &Value) -> bool {
|
2014-06-20 19:01:38 -05:00
|
|
|
match (self, other) {
|
|
|
|
(&String(..), &String(..)) |
|
|
|
|
(&Integer(..), &Integer(..)) |
|
|
|
|
(&Float(..), &Float(..)) |
|
|
|
|
(&Boolean(..), &Boolean(..)) |
|
|
|
|
(&Datetime(..), &Datetime(..)) |
|
|
|
|
(&Array(..), &Array(..)) |
|
|
|
|
(&Table(..), &Table(..)) => true,
|
|
|
|
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-20 19:30:08 -05:00
|
|
|
/// Returns a human-readable representation of the type of this value.
|
|
|
|
pub fn type_str(&self) -> &'static str {
|
2014-06-20 19:01:38 -05:00
|
|
|
match *self {
|
|
|
|
String(..) => "string",
|
|
|
|
Integer(..) => "integer",
|
|
|
|
Float(..) => "float",
|
|
|
|
Boolean(..) => "boolean",
|
|
|
|
Datetime(..) => "datetime",
|
|
|
|
Array(..) => "array",
|
|
|
|
Table(..) => "table",
|
|
|
|
}
|
|
|
|
}
|
2014-06-20 19:30:08 -05:00
|
|
|
|
|
|
|
/// Extracts the string of this value if it is a string.
|
|
|
|
pub fn as_str<'a>(&'a self) -> Option<&'a str> {
|
|
|
|
match *self { String(ref s) => Some(s.as_slice()), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the integer value if it is an integer.
|
|
|
|
pub fn as_integer(&self) -> Option<i64> {
|
|
|
|
match *self { Integer(i) => Some(i), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the float value if it is a float.
|
|
|
|
pub fn as_float(&self) -> Option<f64> {
|
|
|
|
match *self { Float(f) => Some(f), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the boolean value if it is a boolean.
|
|
|
|
pub fn as_bool(&self) -> Option<bool> {
|
|
|
|
match *self { Boolean(b) => Some(b), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the datetime value if it is a datetime.
|
|
|
|
///
|
|
|
|
/// Note that a parsed TOML value will only contain ISO 8601 dates. An
|
|
|
|
/// example date is:
|
|
|
|
///
|
|
|
|
/// ```notrust
|
|
|
|
/// 1979-05-27T07:32:00Z
|
|
|
|
/// ```
|
|
|
|
pub fn as_datetime<'a>(&'a self) -> Option<&'a str> {
|
|
|
|
match *self { Datetime(ref s) => Some(s.as_slice()), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the array value if it is an array.
|
|
|
|
pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
|
|
|
|
match *self { Array(ref s) => Some(s.as_slice()), _ => None }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts the table value if it is a table.
|
|
|
|
pub fn as_table<'a>(&'a self) -> Option<&'a Table> {
|
|
|
|
match *self { Table(ref s) => Some(s), _ => None }
|
|
|
|
}
|
2014-06-25 04:22:11 -05:00
|
|
|
|
|
|
|
/// Lookups for value at specified path.
|
|
|
|
///
|
|
|
|
/// Uses '.' as a path separator.
|
|
|
|
///
|
|
|
|
/// Note: arrays have zero-based indexes.
|
2014-06-25 09:00:10 -05:00
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// let toml = r#"
|
|
|
|
/// [test]
|
|
|
|
/// foo = "bar"
|
|
|
|
///
|
|
|
|
/// [[values]]
|
|
|
|
/// foo = "baz"
|
|
|
|
///
|
|
|
|
/// [[values]]
|
|
|
|
/// foo = "qux"
|
|
|
|
/// "#;
|
|
|
|
/// let value: toml::Value = from_str(toml).unwrap();
|
|
|
|
///
|
|
|
|
/// let foo = value.lookup("test.foo").unwrap();
|
|
|
|
/// assert_eq!(foo.as_str().unwrap(), "bar");
|
|
|
|
///
|
|
|
|
/// let foo = value.lookup("values.1.foo").unwrap();
|
|
|
|
/// assert_eq!(foo.as_str().unwrap(), "qux");
|
|
|
|
///
|
|
|
|
/// let no_bar = value.lookup("test.bar");
|
|
|
|
/// assert_eq!(no_bar.is_none(), true);
|
|
|
|
/// ```
|
2014-06-25 04:22:11 -05:00
|
|
|
pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
|
2014-06-27 09:28:55 -05:00
|
|
|
let mut cur_value = self;
|
|
|
|
for key in path.split('.') {
|
2014-06-25 04:22:11 -05:00
|
|
|
match cur_value {
|
|
|
|
&Table(ref hm) => {
|
2014-07-15 19:30:00 -05:00
|
|
|
match hm.find_with(|k| key.cmp(&k.as_slice())) {
|
2014-06-25 04:22:11 -05:00
|
|
|
Some(v) => cur_value = v,
|
|
|
|
_ => return None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
&Array(ref v) => {
|
|
|
|
let idx: Option<uint> = FromStr::from_str(key);
|
|
|
|
match idx {
|
2014-06-25 09:00:10 -05:00
|
|
|
Some(idx) if idx < v.len() => cur_value = v.get(idx),
|
2014-06-25 04:22:11 -05:00
|
|
|
_ => return None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => return None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(cur_value)
|
|
|
|
}
|
2014-06-20 19:01:38 -05:00
|
|
|
}
|
2014-06-22 01:35:49 -05:00
|
|
|
|
|
|
|
impl FromStr for Value {
|
|
|
|
fn from_str(s: &str) -> Option<Value> {
|
|
|
|
Parser::new(s).parse().map(Table)
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 09:00:10 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2014-06-27 09:28:55 -05:00
|
|
|
mod tests {
|
2014-06-25 09:00:10 -05:00
|
|
|
use super::Value;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn lookup_valid() {
|
|
|
|
let toml = r#"
|
|
|
|
[test]
|
|
|
|
foo = "bar"
|
|
|
|
|
|
|
|
[[values]]
|
|
|
|
foo = "baz"
|
|
|
|
|
|
|
|
[[values]]
|
|
|
|
foo = "qux"
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let value: Value = from_str(toml).unwrap();
|
|
|
|
|
|
|
|
let test_foo = value.lookup("test.foo").unwrap();
|
|
|
|
assert_eq!(test_foo.as_str().unwrap(), "bar");
|
|
|
|
|
|
|
|
let foo1 = value.lookup("values.1.foo").unwrap();
|
|
|
|
assert_eq!(foo1.as_str().unwrap(), "qux");
|
|
|
|
|
|
|
|
let no_bar = value.lookup("test.bar");
|
|
|
|
assert!(no_bar.is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn lookup_invalid_index() {
|
|
|
|
let toml = r#"
|
|
|
|
[[values]]
|
|
|
|
foo = "baz"
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let value: Value = from_str(toml).unwrap();
|
|
|
|
|
|
|
|
let foo = value.lookup("test.foo");
|
|
|
|
assert!(foo.is_none());
|
|
|
|
|
|
|
|
let foo = value.lookup("values.100.foo");
|
|
|
|
assert!(foo.is_none());
|
|
|
|
|
|
|
|
let foo = value.lookup("values.str.foo");
|
|
|
|
assert!(foo.is_none());
|
|
|
|
}
|
|
|
|
}
|