toml-rs/src/lib.rs

261 lines
7.5 KiB
Rust
Raw Normal View History

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-20 19:30:08 -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`
2015-02-03 00:29:51 -06:00
//! * `toml::Value` => `String` - via `Display`
//! * `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
2015-01-29 10:42:02 -06:00
#![feature(collections, core)]
2014-10-31 10:27:24 -05:00
#![deny(missing_docs)]
2014-12-23 10:01:35 -06:00
#![cfg_attr(test, deny(warnings))]
2014-12-23 10:01:35 -06:00
extern crate "rustc-serialize" as rustc_serialize;
2014-06-20 19:01:38 -05:00
2014-12-21 00:35:14 -06:00
use std::collections::BTreeMap;
2014-11-17 02:48:33 -06:00
use std::str::FromStr;
2014-09-21 09:55:13 -05:00
use std::string;
2014-06-20 19:01:38 -05:00
pub use parser::{Parser, ParserError};
pub use serialization::{Encoder, encode, encode_str};
2014-06-23 00:31:09 -05:00
pub use serialization::{Decoder, decode, decode_str};
2014-11-18 02:13:21 -06:00
pub use serialization::Error;
pub use serialization::Error::{NeedsKey, NoValue};
pub use serialization::Error::{InvalidMapKeyLocation, InvalidMapKeyType};
pub use serialization::{DecodeError, DecodeErrorKind};
pub use serialization::DecodeErrorKind::{ApplicationError, ExpectedField};
pub use serialization::DecodeErrorKind::{ExpectedMapElement, ExpectedMapKey, NoEnumVariants};
pub use serialization::DecodeErrorKind::{ExpectedType, NilTooLong};
2014-06-20 19:01:38 -05:00
mod parser;
mod show;
mod serialization;
2015-01-15 14:44:05 -06:00
2014-06-20 19:30:08 -05:00
/// Representation of a TOML value.
2015-02-03 00:29:51 -06:00
#[derive(PartialEq, Clone, Debug)]
2014-10-31 10:27:24 -05:00
#[allow(missing_docs)]
2014-06-20 19:01:38 -05:00
pub enum Value {
2014-09-21 09:55:13 -05:00
String(string::String),
2014-06-20 19:01:38 -05:00
Integer(i64),
Float(f64),
Boolean(bool),
2014-09-21 09:55:13 -05:00
Datetime(string::String),
2014-12-06 16:51:51 -06:00
Array(Array),
Table(Table),
2014-06-20 19:01:38 -05:00
}
2014-12-06 16:48:32 -06:00
/// Type representing a TOML array, payload of the Value::Array variant
pub type Array = Vec<Value>;
/// Type representing a TOML table, payload of the Value::Table variant
2014-12-21 00:35:14 -06:00
pub type Table = BTreeMap<string::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) {
2014-12-06 16:51:51 -06:00
(&Value::String(..), &Value::String(..)) |
(&Value::Integer(..), &Value::Integer(..)) |
(&Value::Float(..), &Value::Float(..)) |
(&Value::Boolean(..), &Value::Boolean(..)) |
(&Value::Datetime(..), &Value::Datetime(..)) |
(&Value::Array(..), &Value::Array(..)) |
(&Value::Table(..), &Value::Table(..)) => true,
2014-06-20 19:01:38 -05:00
_ => 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 {
2014-12-06 16:51:51 -06:00
Value::String(..) => "string",
Value::Integer(..) => "integer",
Value::Float(..) => "float",
Value::Boolean(..) => "boolean",
Value::Datetime(..) => "datetime",
Value::Array(..) => "array",
Value::Table(..) => "table",
2014-06-20 19:01:38 -05:00
}
}
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 { Value::String(ref s) => Some(&**s), _ => None }
2014-06-20 19:30:08 -05:00
}
/// Extracts the integer value if it is an integer.
pub fn as_integer(&self) -> Option<i64> {
2014-12-06 16:51:51 -06:00
match *self { Value::Integer(i) => Some(i), _ => None }
2014-06-20 19:30:08 -05:00
}
/// Extracts the float value if it is a float.
pub fn as_float(&self) -> Option<f64> {
2014-12-06 16:51:51 -06:00
match *self { Value::Float(f) => Some(f), _ => None }
2014-06-20 19:30:08 -05:00
}
/// Extracts the boolean value if it is a boolean.
pub fn as_bool(&self) -> Option<bool> {
2014-12-06 16:51:51 -06:00
match *self { Value::Boolean(b) => Some(b), _ => None }
2014-06-20 19:30:08 -05:00
}
/// 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 { Value::Datetime(ref s) => Some(&**s), _ => None }
2014-06-20 19:30:08 -05:00
}
/// Extracts the array value if it is an array.
pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
match *self { Value::Array(ref s) => Some(&**s), _ => None }
2014-06-20 19:30:08 -05:00
}
/// Extracts the table value if it is a table.
2014-12-06 16:51:51 -06:00
pub fn as_table<'a>(&'a self) -> Option<&'a Table> {
match *self { Value::Table(ref s) => Some(s), _ => None }
2014-06-20 19:30:08 -05:00
}
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
///
/// ```
2015-01-09 13:48:06 -06:00
/// # #![allow(unstable)]
2014-06-25 09:00:10 -05:00
/// let toml = r#"
/// [test]
/// foo = "bar"
///
/// [[values]]
/// foo = "baz"
///
/// [[values]]
/// foo = "qux"
/// "#;
2014-12-23 10:01:35 -06:00
/// let value: toml::Value = toml.parse().unwrap();
2014-06-25 09:00:10 -05:00
///
/// 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 {
2014-12-06 16:51:51 -06:00
&Value::Table(ref hm) => {
2014-12-21 00:35:14 -06:00
match hm.get(key) {
2014-06-25 04:22:11 -05:00
Some(v) => cur_value = v,
2014-12-21 00:35:14 -06:00
None => return None
2014-06-25 04:22:11 -05:00
}
},
2014-12-06 16:51:51 -06:00
&Value::Array(ref v) => {
2015-01-09 13:48:06 -06:00
match key.parse::<usize>() {
Some(idx) if idx < v.len() => cur_value = &v[idx],
2014-06-25 04:22:11 -05:00
_ => return None
}
},
_ => return None
}
};
Some(cur_value)
}
2014-06-20 19:01:38 -05:00
}
impl FromStr for Value {
fn from_str(s: &str) -> Option<Value> {
2014-12-06 16:51:51 -06:00
Parser::new(s).parse().map(Value::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"
"#;
2014-12-23 10:01:35 -06:00
let value: Value = toml.parse().unwrap();
2014-06-25 09:00:10 -05:00
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"
"#;
2014-12-23 10:01:35 -06:00
let value: Value = toml.parse().unwrap();
2014-06-25 09:00:10 -05:00
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());
}
}