use std::str::FromStr;
use chrono::{self, FixedOffset};
use combine::stream::state::State;
use linked_hash_map::LinkedHashMap;
use super::{
decor::{Decor, Formatted, InternalString},
formatted, parser,
table::{Item, KeyValuePairs, TableKeyValue},
};
#[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),
}
#[allow(clippy::enum_variant_names)] #[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 values: Vec<Item>,
pub trailing: InternalString,
pub trailing_comma: bool,
pub newlines: bool,
pub 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,
}
impl Array {
pub fn len(&self) -> usize { self.values.len() }
pub fn is_empty(&self) -> bool { self.len() == 0 }
pub fn iter(&self) -> impl Iterator<Item = &Value> {
self.values.iter().filter_map(Item::as_value)
}
pub fn sort(&mut self) { self.values.sort_by(|a, b| a.as_str().cmp(&b.as_str())) }
pub fn push_formatted(&mut self, v: Value) -> Result<(), Value> {
self.value_op(v, false, |items, value| items.push(Item::Value(value)))
}
pub fn fmt(&mut self, is_compact: bool, multi_trailing_comma: bool) {
formatted::decorate_array(self, is_compact, multi_trailing_comma);
}
fn value_op<T>(
&mut self,
v: Value,
decorate: bool,
op: impl FnOnce(&mut Vec<Item>, Value) -> T,
) -> Result<T, Value> {
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() {
Ok(op(&mut self.values, value))
} else {
Err(value)
}
}
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
}
}
}
impl InlineTable {
pub fn len(&self) -> usize { self.iter().count() }
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
self.items
.iter()
.filter(|&(_, kv)| kv.value.is_value())
.map(|(k, kv)| (&k[..], kv.value.as_value().unwrap()))
}
pub fn contains_key(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) { !kv.value.is_none() } else { false }
}
pub fn fmt(&mut self, is_compact: bool) {
formatted::decorate_inline_table(self, is_compact);
}
}
impl Value {
pub fn as_integer(&self) -> Option<i64> {
match *self {
Value::Integer(ref value) => Some(*value.value()),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match *self {
Value::Boolean(ref value) => Some(*value.value()),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match *self {
Value::String(ref value) => Some(value.value()),
_ => None,
}
}
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(f) => &f.repr.decor,
Value::String(f) => &f.repr.decor,
Value::Float(f) => &f.repr.decor,
Value::DateTime(f) => &f.repr.decor,
Value::Boolean(f) => &f.repr.decor,
Value::Array(a) => &a.decor,
Value::InlineTable(t) => &t.decor,
}
}
pub fn decor_mut(&mut self) -> &mut Decor {
match self {
Value::Integer(f) => &mut f.repr.decor,
Value::String(f) => &mut f.repr.decor,
Value::Float(f) => &mut f.repr.decor,
Value::DateTime(f) => &mut f.repr.decor,
Value::Boolean(f) => &mut f.repr.decor,
Value::Array(a) => &mut a.decor,
Value::InlineTable(t) => &mut t.decor,
}
}
}
pub(crate) fn sort_key_value_pairs(
items: &mut LinkedHashMap<InternalString, TableKeyValue>,
) {
let mut keys: Vec<_> = 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 = crate::toml_edit::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)),
}
}
}