blob: 34e16bf7694856f347de27688e91ee663d7b4486 [file] [log] [blame]
use crate::array_of_tables::ArrayOfTables;
use crate::decor::{Decor, InternalString, Repr};
use crate::formatted::{decorated, key_repr};
use crate::key::Key;
use crate::value::{sort_key_value_pairs, Array, DateTime, InlineTable, Value};
use linked_hash_map::LinkedHashMap;
// TODO: add method to convert a table into inline table
/// Type representing a TOML non-inline table
#[derive(Clone, Debug, Default)]
pub struct Table {
pub(crate) items: KeyValuePairs,
// comments/spaces before and after the header
pub(crate) decor: Decor,
// whether to hide an empty table
pub(crate) implicit: bool,
// used for putting tables back in their original order when serialising.
// Will be None when the Table wasn't parsed from a file.
pub(crate) position: Option<usize>,
}
pub(crate) type KeyValuePairs = LinkedHashMap<InternalString, TableKeyValue>;
/// Type representing either a value, a table, an array of tables, or none.
#[derive(Debug, Clone)]
pub enum Item {
/// Type representing none.
None,
/// Type representing value.
Value(Value),
/// Type representing table.
Table(Table),
/// Type representing array of tables.
ArrayOfTables(ArrayOfTables),
}
impl Default for Item {
fn default() -> Self {
Item::None
}
}
// TODO: make pub(crate)
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct TableKeyValue {
pub(crate) key: Repr,
pub(crate) value: Item,
}
impl TableKeyValue {
pub(crate) fn new(key: Repr, value: Item) -> Self {
TableKeyValue { key, value }
}
}
/// An iterator type over `Table`'s key/value pairs.
pub type Iter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Item)> + 'a>;
/// A mutable iterator type over `Table`'s key/value pairs.
pub type IterMut<'a> = Box<dyn Iterator<Item = (&'a str, &'a mut Item)> + 'a>;
impl Table {
/// Creates an empty table.
pub fn new() -> Self {
Self::with_decor_and_pos(Decor::new("\n", ""), None)
}
pub(crate) fn with_pos(position: Option<usize>) -> Self {
Self {
position,
..Default::default()
}
}
pub(crate) fn with_decor_and_pos(decor: Decor, position: Option<usize>) -> Self {
Self {
decor,
position,
..Default::default()
}
}
/// Returns true iff the table contains an item with the given key.
pub fn contains_key(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) {
!kv.value.is_none()
} else {
false
}
}
/// Returns true iff the table contains a table with the given key.
pub fn contains_table(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) {
kv.value.is_table()
} else {
false
}
}
/// Returns true iff the table contains a value with the given key.
pub fn contains_value(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) {
kv.value.is_value()
} else {
false
}
}
/// Returns true iff the table contains an array of tables with the given key.
pub fn contains_array_of_tables(&self, key: &str) -> bool {
if let Some(kv) = self.items.get(key) {
kv.value.is_array_of_tables()
} else {
false
}
}
/// Returns an iterator over all key/value pairs, including empty.
pub fn iter(&self) -> Iter<'_> {
Box::new(self.items.iter().map(|(key, kv)| (&key[..], &kv.value)))
}
/// Returns an mutable iterator over all key/value pairs, including empty.
pub fn iter_mut(&mut self) -> IterMut<'_> {
Box::new(
self.items
.iter_mut()
.map(|(key, kv)| (&key[..], &mut kv.value)),
)
}
/// Removes an item given the key.
pub fn remove(&mut self, key: &str) -> Option<Item> {
self.items.remove(key).map(|kv| kv.value)
}
/// Sorts Key/Value Pairs of the table,
/// doesn't affect subtables or subarrays.
pub fn sort_values(&mut self) {
sort_key_value_pairs(&mut self.items);
}
/// Returns the number of non-empty items in the table.
pub fn len(&self) -> usize {
self.items.iter().filter(|i| !(i.1).value.is_none()).count()
}
/// Returns the number of key/value pairs in the table.
pub fn values_len(&self) -> usize {
self.items.iter().filter(|i| (i.1).value.is_value()).count()
}
/// Returns true iff the table is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Given the `key`, return a mutable reference to the value.
/// If there is no entry associated with the given key in the table,
/// a `Item::None` value will be inserted.
///
/// To insert to table, use `entry` to return a mutable reference
/// and set it to the appropriate value.
pub fn entry<'a>(&'a mut self, key: &str) -> &'a mut Item {
let parsed_key = key.parse::<Key>().expect("invalid key");
&mut self
.items
.entry(parsed_key.get().to_owned())
.or_insert(TableKeyValue::new(key_repr(parsed_key.raw()), Item::None))
.value
}
/// Returns an optional reference to an item given the key.
pub fn get<'a>(&'a self, key: &str) -> Option<&'a Item> {
self.items.get(key).map(|kv| &kv.value)
}
/// Returns an optional mutable reference to an item given the key.
pub fn get_mut<'a>(&'a mut self, key: &str) -> Option<&'a mut Item> {
self.items.get_mut(key).map(|kv| &mut kv.value)
}
/// If a table has no key/value pairs and implicit, it will not be displayed.
///
/// # Examples
///
/// ```notrust
/// [target."x86_64/windows.json".dependencies]
/// ```
///
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
///
/// ```
/// use toml_edit::Document;
/// let mut doc = "[a]\n[a.b]\n".parse::<Document>().expect("invalid toml");
///
/// doc["a"].as_table_mut().unwrap().set_implicit(true);
/// assert_eq!(doc.to_string(), "[a.b]\n");
/// ```
pub fn set_implicit(&mut self, implicit: bool) {
self.implicit = implicit;
}
/// Returns the decor associated with a given key of the table.
pub fn decor(&self, key: &str) -> Option<&Decor> {
self.items.get(key).map(|kv| &kv.key.decor)
}
/// Sets the position of the `Table` within the `Document`.
///
/// Setting the position of a table will only affect output when
/// `Document::to_string_in_original_order` is used.
pub fn set_position(&mut self, position: usize) {
self.position = Some(position);
}
/// The position of the `Table` within the `Document`.
///
/// Returns `None` if the `Table` was created manually (i.e. not via parsing)
/// in which case its position is set automatically.
pub fn position(&self) -> Option<usize> {
self.position
}
}
impl Item {
/// Sets `self` to the given item iff `self` is none and
/// returns a mutable reference to `self`.
pub fn or_insert(&mut self, item: Item) -> &mut Item {
if self.is_none() {
*self = item
}
self
}
}
// TODO: This should be generated by macro or derive
/// Downcasting
impl Item {
/// Casts `self` to value.
pub fn as_value(&self) -> Option<&Value> {
match *self {
Item::Value(ref v) => Some(v),
_ => None,
}
}
/// Casts `self` to table.
pub fn as_table(&self) -> Option<&Table> {
match *self {
Item::Table(ref t) => Some(t),
_ => None,
}
}
/// Casts `self` to array of tables.
pub fn as_array_of_tables(&self) -> Option<&ArrayOfTables> {
match *self {
Item::ArrayOfTables(ref a) => Some(a),
_ => None,
}
}
/// Casts `self` to mutable value.
pub fn as_value_mut(&mut self) -> Option<&mut Value> {
match *self {
Item::Value(ref mut v) => Some(v),
_ => None,
}
}
/// Casts `self` to mutable table.
pub fn as_table_mut(&mut self) -> Option<&mut Table> {
match *self {
Item::Table(ref mut t) => Some(t),
_ => None,
}
}
/// Casts `self` to mutable array of tables.
pub fn as_array_of_tables_mut(&mut self) -> Option<&mut ArrayOfTables> {
match *self {
Item::ArrayOfTables(ref mut a) => Some(a),
_ => None,
}
}
/// Returns true iff `self` is a value.
pub fn is_value(&self) -> bool {
self.as_value().is_some()
}
/// Returns true iff `self` is a table.
pub fn is_table(&self) -> bool {
self.as_table().is_some()
}
/// Returns true iff `self` is an array of tables.
pub fn is_array_of_tables(&self) -> bool {
self.as_array_of_tables().is_some()
}
/// Returns true iff `self` is `None`.
pub fn is_none(&self) -> bool {
matches!(*self, Item::None)
}
// Duplicate Value downcasting API
/// Casts `self` to integer.
pub fn as_integer(&self) -> Option<i64> {
self.as_value().and_then(Value::as_integer)
}
/// Returns true iff `self` is an integer.
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Casts `self` to float.
pub fn as_float(&self) -> Option<f64> {
self.as_value().and_then(Value::as_float)
}
/// Returns true iff `self` is a float.
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
/// Casts `self` to boolean.
pub fn as_bool(&self) -> Option<bool> {
self.as_value().and_then(Value::as_bool)
}
/// Returns true iff `self` is a boolean.
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
/// Casts `self` to str.
pub fn as_str(&self) -> Option<&str> {
self.as_value().and_then(Value::as_str)
}
/// Returns true iff `self` is a string.
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
/// Casts `self` to date-time.
pub fn as_date_time(&self) -> Option<&DateTime> {
self.as_value().and_then(Value::as_date_time)
}
/// Returns true iff `self` is a date-time.
pub fn is_date_time(&self) -> bool {
self.as_date_time().is_some()
}
/// Casts `self` to array.
pub fn as_array(&self) -> Option<&Array> {
self.as_value().and_then(Value::as_array)
}
/// Casts `self` to mutable array.
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
self.as_value_mut().and_then(Value::as_array_mut)
}
/// Returns true iff `self` is an array.
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
/// Casts `self` to inline table.
pub fn as_inline_table(&self) -> Option<&InlineTable> {
self.as_value().and_then(Value::as_inline_table)
}
/// Casts `self` to mutable inline table.
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
self.as_value_mut().and_then(Value::as_inline_table_mut)
}
/// Returns true iff `self` is an inline table.
pub fn is_inline_table(&self) -> bool {
self.as_inline_table().is_some()
}
/// Casts `self` to either a table or an inline table.
pub fn as_table_like(&self) -> Option<&dyn TableLike> {
self.as_table()
.map(|t| t as &dyn TableLike)
.or_else(|| self.as_inline_table().map(|t| t as &dyn TableLike))
}
/// Returns true iff `self` is either a table, or an inline table.
pub fn is_table_like(&self) -> bool {
self.as_table_like().is_some()
}
}
/// This trait represents either a `Table`, or an `InlineTable`.
pub trait TableLike {
/// Returns an iterator over key/value pairs.
fn iter(&self) -> Iter<'_>;
/// Returns the number of nonempty items.
fn len(&self) -> usize {
self.iter().filter(|&(_, v)| !v.is_none()).count()
}
/// Returns true iff the table is empty.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns an optional reference to an item given the key.
fn get<'s>(&'s self, key: &str) -> Option<&'s Item>;
/// Returns an optional mutable reference to an item given the key.
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item>;
}
impl TableLike for Table {
/// Returns an iterator over all subitems, including `Item::None`.
fn iter(&self) -> Iter<'_> {
self.iter()
}
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
self.get(key)
}
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item> {
self.get_mut(key)
}
}
/// Returns a formatted value.
///
/// Since formatting is part of a `Value`, the right hand side of the
/// assignment needs to be decorated with a space before the value.
/// The `value` function does just that.
///
/// # Examples
/// ```rust
/// # use pretty_assertions::assert_eq;
/// # use toml_edit::*;
/// let mut table = Table::default();
/// let mut array = Array::default();
/// array.push("hello");
/// array.push("\\, world"); // \ is only allowed in a literal string
/// table["key1"] = value("value1");
/// table["key2"] = value(42);
/// table["key3"] = value(array);
/// assert_eq!(table.to_string(),
/// r#"key1 = "value1"
/// key2 = 42
/// key3 = ["hello", '\, world']
/// "#);
/// ```
pub fn value<V: Into<Value>>(v: V) -> Item {
Item::Value(decorated(v.into(), " ", ""))
}
/// Returns an empty table.
pub fn table() -> Item {
Item::Table(Table::new())
}
/// Returns an empty array of tables.
pub fn array() -> Item {
Item::ArrayOfTables(ArrayOfTables::new())
}