blob: 6e9df5abd123ee2cab3d086b464a0d2a5eb3fccc [file] [log] [blame]
//! Definition of a TOML value
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
use std::mem::discriminant;
use std::ops;
use std::str::FromStr;
pub use crate::easy::datetime::*;
use crate::easy::map::Entry;
pub use crate::easy::map::Map;
/// Representation of a TOML value.
#[derive(PartialEq, Clone, Debug, serde::Serialize)]
#[serde(untagged)]
pub enum Value {
/// Represents a TOML integer
Integer(i64),
/// Represents a TOML float
Float(f64),
/// Represents a TOML boolean
Boolean(bool),
/// Represents a TOML datetime
Datetime(Datetime),
/// Represents a TOML string
String(String),
/// Represents a TOML array
Array(Array),
/// Represents a TOML table
Table(Table),
}
/// 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.
/// By default it is backed by a BTreeMap, enable the `preserve_order` feature
/// to use a LinkedHashMap instead.
pub type Table = Map<String, Value>;
impl Value {
/// Convert a `T` into `toml::Value` which is an enum that can represent
/// any valid TOML data.
///
/// This conversion can fail if `T`'s implementation of `Serialize` decides to
/// fail, or if `T` contains a map with non-string keys.
pub fn try_from<T>(value: T) -> Result<Value, crate::TomlError>
where
T: serde::ser::Serialize,
{
let item = super::ser::to_item(&value)?;
let value = super::de::from_item(item)?;
Ok(value)
}
/// Interpret a `toml::Value` as an instance of type `T`.
///
/// This conversion can fail if the structure of the `Value` does not match the
/// structure expected by `T`, for example if `T` is a struct type but the
/// `Value` contains something other than a TOML table. It can also fail if the
/// structure is correct but `T`'s implementation of `Deserialize` decides that
/// something is wrong with the data, for example required struct fields are
/// missing from the TOML map or some number is too big to fit in the expected
/// primitive type.
pub fn try_into<T>(self) -> Result<T, crate::TomlError>
where
T: serde::de::DeserializeOwned,
{
let item = super::ser::to_item(&self)?;
let value = super::de::from_item(item)?;
Ok(value)
}
/// Index into a TOML array or map. A string index can be used to access a
/// value in a map, and a usize index can be used to access an element of an
/// array.
///
/// Returns `None` if the type of `self` does not match the type of the
/// index, for example if the index is a string and `self` is an array or a
/// number. Also returns `None` if the given key does not exist in the map
/// or the given index is not within the bounds of the array.
pub fn get<I: Index>(&self, index: I) -> Option<&Value> {
index.index(self)
}
/// Mutably index into a TOML array or map. A string index can be used to
/// access a value in a map, and a usize index can be used to access an
/// element of an array.
///
/// Returns `None` if the type of `self` does not match the type of the
/// index, for example if the index is a string and `self` is an array or a
/// number. Also returns `None` if the given key does not exist in the map
/// or the given index is not within the bounds of the array.
pub fn get_mut<I: Index>(&mut self, index: I) -> Option<&mut Value> {
index.index_mut(self)
}
/// Extracts the integer value if it is an integer.
pub fn as_integer(&self) -> Option<i64> {
match *self {
Value::Integer(i) => Some(i),
_ => None,
}
}
/// Tests whether this value is an integer.
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Extracts the float value if it is a float.
pub fn as_float(&self) -> Option<f64> {
match *self {
Value::Float(f) => Some(f),
_ => None,
}
}
/// Tests whether this value is a float.
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
/// Extracts the boolean value if it is a boolean.
pub fn as_bool(&self) -> Option<bool> {
match *self {
Value::Boolean(b) => Some(b),
_ => None,
}
}
/// Tests whether this value is a boolean.
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
/// Extracts the string of this value if it is a string.
pub fn as_str(&self) -> Option<&str> {
match *self {
Value::String(ref s) => Some(&**s),
_ => None,
}
}
/// Tests if this value is a string.
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
/// 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(&self) -> Option<&Datetime> {
match *self {
Value::Datetime(ref s) => Some(s),
_ => None,
}
}
/// Tests whether this value is a datetime.
pub fn is_datetime(&self) -> bool {
self.as_datetime().is_some()
}
/// Extracts the array value if it is an array.
pub fn as_array(&self) -> Option<&Vec<Value>> {
match *self {
Value::Array(ref s) => Some(s),
_ => None,
}
}
/// Extracts the array value if it is an array.
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
match *self {
Value::Array(ref mut s) => Some(s),
_ => None,
}
}
/// Tests whether this value is an array.
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
/// Extracts the table value if it is a table.
pub fn as_table(&self) -> Option<&Table> {
match *self {
Value::Table(ref s) => Some(s),
_ => None,
}
}
/// Extracts the table value if it is a table.
pub fn as_table_mut(&mut self) -> Option<&mut Table> {
match *self {
Value::Table(ref mut s) => Some(s),
_ => None,
}
}
/// Tests whether this value is a table.
pub fn is_table(&self) -> bool {
self.as_table().is_some()
}
/// Tests whether this and another value have the same type.
pub fn same_type(&self, other: &Value) -> bool {
discriminant(self) == discriminant(other)
}
/// Returns a human-readable representation of the type of this value.
pub fn type_str(&self) -> &'static str {
match *self {
Value::String(..) => "string",
Value::Integer(..) => "integer",
Value::Float(..) => "float",
Value::Boolean(..) => "boolean",
Value::Datetime(..) => "datetime",
Value::Array(..) => "array",
Value::Table(..) => "table",
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Value::String(..)
| Value::Integer(..)
| Value::Float(..)
| Value::Boolean(..)
| Value::Datetime(..)
| Value::Array(..) => crate::ser::to_item(self)
.map_err(|_| std::fmt::Error)?
.fmt(f),
Value::Table(_) => crate::ser::to_string_pretty(self)
.map_err(|_| std::fmt::Error)?
.fmt(f),
}
}
}
impl<I> ops::Index<I> for Value
where
I: Index,
{
type Output = Value;
fn index(&self, index: I) -> &Value {
self.get(index).expect("index not found")
}
}
impl<I> ops::IndexMut<I> for Value
where
I: Index,
{
fn index_mut(&mut self, index: I) -> &mut Value {
self.get_mut(index).expect("index not found")
}
}
impl<'a> From<&'a str> for Value {
#[inline]
fn from(val: &'a str) -> Value {
Value::String(val.to_string())
}
}
impl<V: Into<Value>> From<Vec<V>> for Value {
fn from(val: Vec<V>) -> Value {
Value::Array(val.into_iter().map(|v| v.into()).collect())
}
}
impl<S: Into<String>, V: Into<Value>> From<BTreeMap<S, V>> for Value {
fn from(val: BTreeMap<S, V>) -> Value {
let table = val.into_iter().map(|(s, v)| (s.into(), v.into())).collect();
Value::Table(table)
}
}
impl<S: Into<String> + Hash + Eq, V: Into<Value>> From<HashMap<S, V>> for Value {
fn from(val: HashMap<S, V>) -> Value {
let table = val.into_iter().map(|(s, v)| (s.into(), v.into())).collect();
Value::Table(table)
}
}
macro_rules! impl_into_value {
($variant:ident : $T:ty) => {
impl From<$T> for Value {
#[inline]
fn from(val: $T) -> Value {
Value::$variant(val.into())
}
}
};
}
impl_into_value!(String: String);
impl_into_value!(Integer: i64);
impl_into_value!(Integer: i32);
impl_into_value!(Integer: i8);
impl_into_value!(Integer: u8);
impl_into_value!(Integer: u32);
impl_into_value!(Float: f64);
impl_into_value!(Float: f32);
impl_into_value!(Boolean: bool);
impl_into_value!(Datetime: Datetime);
impl_into_value!(Table: Table);
/// Types that can be used to index a `toml_edit::easy::Value`
///
/// Currently this is implemented for `usize` to index arrays and `str` to index
/// tables.
///
/// This trait is sealed and not intended for implementation outside of the
/// `toml` crate.
pub trait Index: crate::private::Sealed {
#[doc(hidden)]
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value>;
#[doc(hidden)]
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value>;
}
impl Index for usize {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
match *val {
Value::Array(ref a) => a.get(*self),
_ => None,
}
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
match *val {
Value::Array(ref mut a) => a.get_mut(*self),
_ => None,
}
}
}
impl Index for str {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
match *val {
Value::Table(ref a) => a.get(self),
_ => None,
}
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
match *val {
Value::Table(ref mut a) => a.get_mut(self),
_ => None,
}
}
}
impl Index for String {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
self[..].index(val)
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
self[..].index_mut(val)
}
}
impl<'s, T: ?Sized> Index for &'s T
where
T: Index,
{
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
(**self).index(val)
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
(**self).index_mut(val)
}
}
impl FromStr for Value {
type Err = crate::easy::de::Error;
fn from_str(s: &str) -> Result<Value, Self::Err> {
crate::easy::from_str(s)
}
}
impl<'de> serde::de::Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct ValueVisitor;
impl<'de> serde::de::Visitor<'de> for ValueVisitor {
type Value = Value;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("any valid TOML value")
}
fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
Ok(Value::Boolean(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
Ok(Value::Integer(value))
}
fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Value, E> {
if value <= i64::max_value() as u64 {
Ok(Value::Integer(value as i64))
} else {
Err(serde::de::Error::custom("u64 value was too large"))
}
}
fn visit_u32<E>(self, value: u32) -> Result<Value, E> {
Ok(Value::Integer(value.into()))
}
fn visit_i32<E>(self, value: i32) -> Result<Value, E> {
Ok(Value::Integer(value.into()))
}
fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
Ok(Value::Float(value))
}
fn visit_str<E>(self, value: &str) -> Result<Value, E> {
Ok(Value::String(value.into()))
}
fn visit_string<E>(self, value: String) -> Result<Value, E> {
Ok(Value::String(value))
}
fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
serde::de::Deserialize::deserialize(deserializer)
}
fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
where
V: serde::de::SeqAccess<'de>,
{
let mut vec = Vec::new();
while let Some(elem) = visitor.next_element()? {
vec.push(elem);
}
Ok(Value::Array(vec))
}
fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut key = String::new();
let datetime = visitor.next_key_seed(DatetimeOrTable { key: &mut key })?;
match datetime {
Some(true) => {
let date: String = visitor.next_value()?;
let date = date.parse::<Datetime>().map_err(serde::de::Error::custom)?;
return Ok(Value::Datetime(date));
}
None => return Ok(Value::Table(Map::new())),
Some(false) => {}
}
let mut map = Map::new();
map.insert(key, visitor.next_value()?);
while let Some(key) = visitor.next_key::<String>()? {
if let Entry::Vacant(vacant) = map.entry(&key) {
vacant.insert(visitor.next_value()?);
} else {
let msg = format!("duplicate key: `{}`", key);
return Err(serde::de::Error::custom(msg));
}
}
Ok(Value::Table(map))
}
}
deserializer.deserialize_any(ValueVisitor)
}
}
struct DatetimeOrTable<'a> {
key: &'a mut String,
}
impl<'a, 'de> serde::de::DeserializeSeed<'de> for DatetimeOrTable<'a> {
type Value = bool;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserializer.deserialize_any(self)
}
}
impl<'a, 'de> serde::de::Visitor<'de> for DatetimeOrTable<'a> {
type Value = bool;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a string key")
}
fn visit_str<E>(self, s: &str) -> Result<bool, E>
where
E: serde::de::Error,
{
if s == toml_datetime::__unstable::FIELD {
Ok(true)
} else {
self.key.push_str(s);
Ok(false)
}
}
fn visit_string<E>(self, s: String) -> Result<bool, E>
where
E: serde::de::Error,
{
if s == toml_datetime::__unstable::FIELD {
Ok(true)
} else {
*self.key = s;
Ok(false)
}
}
}