use crate::decor::{Decor, Formatted, InternalString};
use crate::formatted;
use crate::key::Key;
use crate::parser;
use crate::table::{Item, Iter, KeyValuePairs, TableKeyValue, TableLike};
use chrono::{self, FixedOffset};
use combine::stream::state::State;
use linked_hash_map::LinkedHashMap;
use std::mem;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum Value {
Integer(Formatted<i64>),
String(Formatted<String>),
Float(Formatted<f64>),
DateTime(Formatted<DateTime>),
Boolean(Formatted<bool>),
Array(Array),
InlineTable(InlineTable),
}
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
pub enum DateTime {
OffsetDateTime(chrono::DateTime<FixedOffset>),
LocalDateTime(chrono::NaiveDateTime),
LocalDate(chrono::NaiveDate),
LocalTime(chrono::NaiveTime),
}
#[derive(Debug, Default, Clone)]
pub struct Array {
pub(crate) values: Vec<Item>,
pub(crate) trailing: InternalString,
pub(crate) trailing_comma: bool,
pub(crate) decor: Decor,
}
#[derive(Debug, Default, Clone)]
pub struct InlineTable {
pub(crate) items: KeyValuePairs,
pub(crate) preamble: InternalString,
pub(crate) decor: Decor,
}
#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
pub(crate) enum ValueType {
None,
Integer,
String,
Float,
DateTime,
Boolean,
Array,
InlineTable,
}
pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
impl Array {
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> ArrayIter {
Box::new(self.values.iter().filter_map(Item::as_value))
}
pub fn push<V: Into<Value>>(&mut self, v: V) -> bool {
self.push_value(v.into(), true)
}
pub fn get(&mut self, index: usize) -> Option<&Value> {
self.values.get(index).and_then(Item::as_value)
}
pub fn remove(&mut self, index: usize) -> Value {
let removed = self.values.remove(index);
if self.is_empty() {
self.trailing_comma = false;
}
match removed {
Item::Value(v) => v,
x => panic!("non-value item {:?} in an array", x),
}
}
pub fn fmt(&mut self) {
formatted::decorate_array(self);
}
pub(crate) fn push_value(&mut self, v: Value, decorate: bool) -> bool {
let mut value = v;
if !self.is_empty() && decorate {
formatted::decorate(&mut value, " ", "");
} else if decorate {
formatted::decorate(&mut value, "", "");
}
if self.is_empty() || value.get_type() == self.value_type() {
self.values.push(Item::Value(value));
true
} else {
false
}
}
pub(crate) fn value_type(&self) -> ValueType {
if let Some(value) = self.values.get(0).and_then(Item::as_value) {
value.get_type()
} else {
ValueType::None
}
}
}
pub type InlineTableIter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Value)> + 'a>;
impl InlineTable {
pub fn len(&self) -> usize {
self.iter().count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> InlineTableIter {
Box::new(
self.items
.iter()
.filter(|&(_, kv)| kv.value.is_value())
.map(|(k, kv)| (&k[..], kv.value.as_value().unwrap())),
)
}
pub fn sort(&mut self) {
sort_key_value_pairs(&mut self.items);
}
pub fn contains_key(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) {
!kv.value.is_none()
} else {
false
}
}
pub fn merge_into(&mut self, other: &mut InlineTable) {
let items = mem::replace(&mut self.items, KeyValuePairs::new());
for (k, kv) in items {
other.items.insert(k, kv);
}
}
pub fn get_or_insert<V: Into<Value>>(&mut self, key: &str, value: V) -> &mut Value {
let parsed = key.parse::<Key>().expect("invalid key");
self.items
.entry(parsed.get().to_owned())
.or_insert(formatted::to_key_value(key, value.into()))
.value
.as_value_mut()
.expect("non-value type in inline table")
}
pub fn fmt(&mut self) {
formatted::decorate_inline_table(self);
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
self.items
.remove(key)
.and_then(|kv| kv.value.as_value().cloned())
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.items.get(key).and_then(|kv| kv.value.as_value())
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
self.items
.get_mut(key)
.and_then(|kv| kv.value.as_value_mut())
}
}
impl TableLike for InlineTable {
fn iter(&self) -> Iter {
Box::new(self.items.iter().map(|(key, kv)| (&key[..], &kv.value)))
}
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
self.items.get(key).map(|kv| &kv.value)
}
}
impl DateTime {
pub fn as_offset_date_time(&self) -> Option<&chrono::DateTime<FixedOffset>> {
match *self {
DateTime::OffsetDateTime(ref dt) => Some(dt),
_ => None,
}
}
pub fn as_local_date_time(&self) -> Option<&chrono::NaiveDateTime> {
match *self {
DateTime::LocalDateTime(ref dt) => Some(dt),
_ => None,
}
}
pub fn as_local_date(&self) -> Option<&chrono::NaiveDate> {
match *self {
DateTime::LocalDate(ref d) => Some(d),
_ => None,
}
}
pub fn as_local_time(&self) -> Option<&chrono::NaiveTime> {
match *self {
DateTime::LocalTime(ref t) => Some(t),
_ => None,
}
}
pub fn is_offset_date_time(&self) -> bool {
self.as_offset_date_time().is_some()
}
pub fn is_local_date_time(&self) -> bool {
self.as_local_date_time().is_some()
}
pub fn is_local_date(&self) -> bool {
self.as_local_date().is_some()
}
pub fn is_local_time(&self) -> bool {
self.as_local_time().is_some()
}
}
impl Value {
pub fn as_integer(&self) -> Option<i64> {
match *self {
Value::Integer(ref value) => Some(*value.value()),
_ => None,
}
}
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
pub fn as_float(&self) -> Option<f64> {
match *self {
Value::Float(ref value) => Some(*value.value()),
_ => None,
}
}
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
pub fn as_bool(&self) -> Option<bool> {
match *self {
Value::Boolean(ref value) => Some(*value.value()),
_ => None,
}
}
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
pub fn as_str(&self) -> Option<&str> {
match *self {
Value::String(ref value) => Some(value.value()),
_ => None,
}
}
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
pub fn as_date_time(&self) -> Option<&DateTime> {
match *self {
Value::DateTime(ref value) => Some(value.value()),
_ => None,
}
}
pub fn is_date_time(&self) -> bool {
self.as_date_time().is_some()
}
pub fn as_array(&self) -> Option<&Array> {
match *self {
Value::Array(ref value) => Some(value),
_ => None,
}
}
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
match *self {
Value::Array(ref mut value) => Some(value),
_ => None,
}
}
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
pub fn as_inline_table(&self) -> Option<&InlineTable> {
match *self {
Value::InlineTable(ref value) => Some(value),
_ => None,
}
}
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
match *self {
Value::InlineTable(ref mut value) => Some(value),
_ => None,
}
}
pub fn is_inline_table(&self) -> bool {
self.as_inline_table().is_some()
}
pub(crate) fn get_type(&self) -> ValueType {
match *self {
Value::Integer(..) => ValueType::Integer,
Value::String(..) => ValueType::String,
Value::Float(..) => ValueType::Float,
Value::DateTime(..) => ValueType::DateTime,
Value::Boolean(..) => ValueType::Boolean,
Value::Array(..) => ValueType::Array,
Value::InlineTable(..) => ValueType::InlineTable,
}
}
}
impl Value {
pub fn decor(&self) -> &Decor {
match *self {
Value::Integer(ref f) => &f.repr.decor,
Value::String(ref f) => &f.repr.decor,
Value::Float(ref f) => &f.repr.decor,
Value::DateTime(ref f) => &f.repr.decor,
Value::Boolean(ref f) => &f.repr.decor,
Value::Array(ref a) => &a.decor,
Value::InlineTable(ref t) => &t.decor,
}
}
}
pub(crate) fn sort_key_value_pairs(items: &mut LinkedHashMap<InternalString, TableKeyValue>) {
let mut keys: Vec<InternalString> = items
.iter()
.filter_map(|i| (i.1).value.as_value().map(|_| i.0))
.cloned()
.collect();
keys.sort();
for key in keys {
items.get_refresh(&key);
}
}
impl FromStr for Value {
type Err = parser::TomlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use combine::Parser;
let parsed = parser::value_parser().easy_parse(State::new(s));
match parsed {
Ok((_, ref rest)) if !rest.input.is_empty() => {
Err(Self::Err::from_unparsed(rest.positioner, s))
}
Ok((value, _)) => Ok(value),
Err(e) => Err(Self::Err::new(e, s)),
}
}
}