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 entry(&mut self, key: impl Into<String>) -> Entry<'_> {
let key = key.into();
match self.index.get(&key).copied() {
Some(index) => Entry::Occupied(OccupiedEntry { table: self, index }),
None => Entry::Vacant(VacantEntry { table: self, key }),
}
}
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&str, &mut Value) -> bool,
{
self.entries.retain_mut(|(key, value)| keep(key, value));
self.reindex();
}
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)]
pub enum Entry<'a> {
Vacant(VacantEntry<'a>),
Occupied(OccupiedEntry<'a>),
}
impl<'a> Entry<'a> {
pub fn key(&self) -> &str {
match self {
Entry::Vacant(entry) => entry.key(),
Entry::Occupied(entry) => entry.key(),
}
}
pub fn or_insert(self, default: impl Into<Value>) -> &'a mut Value {
match self {
Entry::Vacant(entry) => entry.insert(default),
Entry::Occupied(entry) => entry.into_mut(),
}
}
pub fn or_insert_with<V: Into<Value>, F: FnOnce() -> V>(self, default: F) -> &'a mut Value {
match self {
Entry::Vacant(entry) => entry.insert(default()),
Entry::Occupied(entry) => entry.into_mut(),
}
}
pub fn and_modify<F: FnOnce(&mut Value)>(self, f: F) -> Entry<'a> {
match self {
Entry::Vacant(entry) => Entry::Vacant(entry),
Entry::Occupied(mut entry) => {
f(entry.get_mut());
Entry::Occupied(entry)
}
}
}
}
#[derive(Debug)]
pub struct VacantEntry<'a> {
table: &'a mut Table,
key: String,
}
impl<'a> VacantEntry<'a> {
pub fn key(&self) -> &str {
&self.key
}
pub fn into_key(self) -> String {
self.key
}
pub fn insert(self, value: impl Into<Value>) -> &'a mut Value {
self.table.insert(self.key, value);
&mut self.table.entries.last_mut().expect("just inserted").1
}
}
#[derive(Debug)]
pub struct OccupiedEntry<'a> {
table: &'a mut Table,
index: usize,
}
impl<'a> OccupiedEntry<'a> {
pub fn key(&self) -> &str {
&self.table.entries[self.index].0
}
pub fn get(&self) -> &Value {
&self.table.entries[self.index].1
}
pub fn get_mut(&mut self) -> &mut Value {
&mut self.table.entries[self.index].1
}
pub fn into_mut(self) -> &'a mut Value {
&mut self.table.entries[self.index].1
}
pub fn insert(&mut self, value: impl Into<Value>) -> Value {
core::mem::replace(self.get_mut(), value.into())
}
pub fn remove(self) -> Value {
let key = self.table.entries[self.index].0.clone();
self.table.remove(&key).expect("the entry is occupied")
}
}
#[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 entry_api() {
let mut table = Table::new();
assert_eq!(table.entry("a").key(), "a");
table.entry("a").or_insert(1);
table.entry("a").or_insert(2);
assert_eq!(table["a"], Value::Integer(1));
table.entry("a").and_modify(|v| *v = Value::Integer(3));
assert_eq!(table["a"], Value::Integer(3));
match table.entry("b") {
Entry::Vacant(entry) => assert_eq!(entry.into_key(), "b"),
Entry::Occupied(_) => panic!("`b` should be vacant"),
}
table.insert("b", 2);
match table.entry("a") {
Entry::Occupied(entry) => assert_eq!(entry.remove(), Value::Integer(3)),
Entry::Vacant(_) => panic!("`a` should be occupied"),
}
assert_eq!(table.get("b"), Some(&Value::Integer(2)));
assert_eq!(table.len(), 1);
}
#[test]
fn retain_keeps_order_and_reindexes() {
let mut table: Table = [("a", 1), ("b", 2), ("c", 3)].into_iter().collect();
table.retain(|_, value| value.as_integer() != Some(2));
assert_eq!(table.keys().collect::<Vec<_>>(), ["a", "c"]);
assert_eq!(table.get("c"), Some(&Value::Integer(3)));
assert_eq!(table.get("b"), None);
}
#[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)));
}
}