use core::fmt;
use core::ops::Index;
use std::collections::HashMap;
use crate::value::Value;
#[derive(Clone, Default)]
pub struct Table {
entries: Vec<(String, Value)>,
index: HashMap<String, usize>,
}
impl Table {
pub fn new() -> Table {
Table::default()
}
pub fn with_capacity(capacity: usize) -> Table {
Table {
entries: Vec::with_capacity(capacity),
index: HashMap::with_capacity(capacity),
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn contains_key(&self, key: &str) -> bool {
self.index.contains_key(key)
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.index.get(key).map(|&i| &self.entries[i].1)
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
match self.index.get(key) {
Some(&i) => Some(&mut self.entries[i].1),
None => None,
}
}
pub fn get_key_value(&self, key: &str) -> Option<(&str, &Value)> {
self.index.get(key).map(|&i| {
let (k, v) = &self.entries[i];
(k.as_str(), v)
})
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Value>) -> Option<Value> {
let key = key.into();
let value = value.into();
match self.index.get(&key) {
Some(&i) => Some(core::mem::replace(&mut self.entries[i].1, value)),
None => {
self.index.insert(key.clone(), self.entries.len());
self.entries.push((key, value));
None
}
}
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
let i = self.index.remove(key)?;
let (_, value) = self.entries.remove(i);
for slot in self.index.values_mut() {
if *slot > i {
*slot -= 1;
}
}
Some(value)
}
pub fn clear(&mut self) {
self.entries.clear();
self.index.clear();
}
pub fn sort_keys(&mut self) {
self.entries.sort_by(|a, b| a.0.cmp(&b.0));
self.reindex();
}
fn reindex(&mut self) {
self.index.clear();
for (i, (key, _)) in self.entries.iter().enumerate() {
self.index.insert(key.clone(), i);
}
}
pub fn get_path(&self, path: &str) -> Option<&Value> {
let mut segments = path.split('.');
let mut current = self.get(segments.next()?)?;
for segment in segments {
current = match current {
Value::Table(table) => table.get(segment)?,
Value::Array(array) => array.get(segment.parse::<usize>().ok()?)?,
_ => return None,
};
}
Some(current)
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.entries.iter(),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut {
inner: self.entries.iter_mut(),
}
}
pub fn keys(&self) -> Keys<'_> {
Keys {
inner: self.entries.iter(),
}
}
pub fn values(&self) -> Values<'_> {
Values {
inner: self.entries.iter(),
}
}
pub fn values_mut(&mut self) -> ValuesMut<'_> {
ValuesMut {
inner: self.entries.iter_mut(),
}
}
}
impl fmt::Debug for Table {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl PartialEq for Table {
fn eq(&self, other: &Table) -> bool {
self.len() == other.len() && self.iter().all(|(k, v)| other.get(k) == Some(v))
}
}
impl Index<&str> for Table {
type Output = Value;
fn index(&self, key: &str) -> &Value {
self.get(key)
.unwrap_or_else(|| panic!("no such key in TOML table: `{key}`"))
}
}
impl<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Table {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Table {
let iter = iter.into_iter();
let mut table = Table::with_capacity(iter.size_hint().0);
for (key, value) in iter {
table.insert(key, value);
}
table
}
}
impl<K: Into<String>, V: Into<Value>> Extend<(K, V)> for Table {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
for (key, value) in iter {
self.insert(key, value);
}
}
}
#[derive(Debug, Clone)]
pub struct Iter<'a> {
inner: core::slice::Iter<'a, (String, Value)>,
}
#[derive(Debug)]
pub struct IterMut<'a> {
inner: core::slice::IterMut<'a, (String, Value)>,
}
#[derive(Debug)]
pub struct IntoIter {
inner: std::vec::IntoIter<(String, Value)>,
}
#[derive(Debug, Clone)]
pub struct Keys<'a> {
inner: core::slice::Iter<'a, (String, Value)>,
}
#[derive(Debug, Clone)]
pub struct Values<'a> {
inner: core::slice::Iter<'a, (String, Value)>,
}
#[derive(Debug)]
pub struct ValuesMut<'a> {
inner: core::slice::IterMut<'a, (String, Value)>,
}
macro_rules! forward_iterator {
($name:ty, $item:ty, $map:expr) => {
impl<'a> Iterator for $name {
type Item = $item;
fn next(&mut self) -> Option<$item> {
self.inner.next().map($map)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> DoubleEndedIterator for $name {
fn next_back(&mut self) -> Option<$item> {
self.inner.next_back().map($map)
}
}
impl<'a> ExactSizeIterator for $name {
fn len(&self) -> usize {
self.inner.len()
}
}
};
}
forward_iterator!(Iter<'a>, (&'a str, &'a Value), |(k, v)| (k.as_str(), v));
forward_iterator!(IterMut<'a>, (&'a str, &'a mut Value), |(k, v)| (
k.as_str(),
v
));
forward_iterator!(Keys<'a>, &'a str, |(k, _)| k.as_str());
forward_iterator!(Values<'a>, &'a Value, |(_, v)| v);
forward_iterator!(ValuesMut<'a>, &'a mut Value, |(_, v)| v);
impl Iterator for IntoIter {
type Item = (String, Value);
fn next(&mut self) -> Option<(String, Value)> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl DoubleEndedIterator for IntoIter {
fn next_back(&mut self) -> Option<(String, Value)> {
self.inner.next_back()
}
}
impl ExactSizeIterator for IntoIter {
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'a> IntoIterator for &'a Table {
type Item = (&'a str, &'a Value);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut Table {
type Item = (&'a str, &'a mut Value);
type IntoIter = IterMut<'a>;
fn into_iter(self) -> IterMut<'a> {
self.iter_mut()
}
}
impl IntoIterator for Table {
type Item = (String, Value);
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
IntoIter {
inner: self.entries.into_iter(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keeps_insertion_order() {
let mut table = Table::new();
table.insert("z", 1);
table.insert("a", 2);
table.insert("m", 3);
assert_eq!(table.keys().collect::<Vec<_>>(), ["z", "a", "m"]);
table.insert("z", 9);
assert_eq!(table.keys().collect::<Vec<_>>(), ["z", "a", "m"]);
assert_eq!(table["z"], Value::Integer(9));
assert_eq!(table.len(), 3);
}
#[test]
fn remove_keeps_the_index_consistent() {
let mut table: Table = [("a", 1), ("b", 2), ("c", 3)].into_iter().collect();
assert_eq!(table.remove("b"), Some(Value::Integer(2)));
assert_eq!(table.remove("b"), None);
assert_eq!(table.get("c"), Some(&Value::Integer(3)));
assert_eq!(table.keys().collect::<Vec<_>>(), ["a", "c"]);
table.insert("d", 4);
assert_eq!(table.get("d"), Some(&Value::Integer(4)));
assert_eq!(table.get("a"), Some(&Value::Integer(1)));
}
#[test]
fn equality_ignores_order() {
let a: Table = [("x", 1), ("y", 2)].into_iter().collect();
let b: Table = [("y", 2), ("x", 1)].into_iter().collect();
assert_eq!(a, b);
let c: Table = [("y", 2)].into_iter().collect();
assert_ne!(a, c);
}
#[test]
fn get_path_walks_tables_and_arrays() {
let mut table = Table::new();
table.insert("a", Value::Array(vec![Value::from([1, 2])]));
table.insert("b.c", 3);
assert_eq!(table.get_path("a.0.1"), Some(&Value::Integer(2)));
assert_eq!(table.get_path("a.0.2"), None);
assert_eq!(table.get_path("a.x"), None);
assert_eq!(table.get_path("nope"), None);
assert_eq!(table.get_path("b.c"), None);
assert_eq!(table.get("b.c"), Some(&Value::Integer(3)));
}
#[test]
fn sort_keys_reindexes() {
let mut table: Table = [("c", 3), ("a", 1), ("b", 2)].into_iter().collect();
table.sort_keys();
assert_eq!(table.keys().collect::<Vec<_>>(), ["a", "b", "c"]);
assert_eq!(table.get("c"), Some(&Value::Integer(3)));
}
}