blob: a88f23edb5fd4dbadbb1b3b81d6144d6b535e68d [file] [log] [blame] [edit]
//! Defines the [`Table`] type.
use {
crate::crate_prelude::*,
std::{collections::HashMap, ops::Deref},
};
/// A set of key/value pairs in TOML.
#[derive(Debug, PartialEq, Default)]
pub struct Table<'a> {
pub(crate) map: HashMap<CowSpan<'a>, TomlValue<'a>>,
}
impl<'a> Table<'a> {
/// Gets the value for a key, if that value is a table.
pub fn get_table(&self, key: &str) -> Result<&Self, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => {
if let TomlValue::Table(table) = val {
Ok(table)
} else {
Err(TomlGetError::TypeMismatch(val, val.value_type()))
}
}
}
}
/// Gets the value for a key, if that value is a string.
pub fn get_string(&self, key: &str) -> Result<&str, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => match val {
TomlValue::String(string) => Ok(string.as_str()),
other_val => Err(TomlGetError::TypeMismatch(
other_val,
other_val.value_type(),
)),
},
}
}
/// Gets the value for a key, if that value is an integer.
pub fn get_integer(&self, key: &str) -> Result<i64, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => {
if let TomlValue::Integer(int) = val {
Ok(*int)
} else {
Err(TomlGetError::TypeMismatch(val, val.value_type()))
}
}
}
}
/// Gets the value for a key, if that value is a float.
pub fn get_float(&self, key: &str) -> Result<f64, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => {
if let TomlValue::Float(float) = val {
Ok(*float)
} else {
Err(TomlGetError::TypeMismatch(val, val.value_type()))
}
}
}
}
/// Gets the value for a key, if that value is a boolean.
pub fn get_boolean(&self, key: &str) -> Result<bool, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => {
if let TomlValue::Boolean(bool) = val {
Ok(*bool)
} else {
Err(TomlGetError::TypeMismatch(val, val.value_type()))
}
}
}
}
/// Gets the value for a key, if that value is an array.
pub fn get_array(&self, key: &str) -> Result<&Vec<TomlValue<'a>>, TomlGetError<'_, 'a>> {
match self.get(key) {
None => Err(TomlGetError::InvalidKey),
Some(ref val) => {
if let TomlValue::Array(array) = val {
Ok(array)
} else {
Err(TomlGetError::TypeMismatch(val, val.value_type()))
}
}
}
}
/// Inserts a value into the table, handling dotted keys automatically. Returns true if
/// inserting the value overwrote another value.
pub(crate) fn insert(&mut self, key: Key<'a>, value: TomlValue<'a>) -> bool {
if let Some(child) = key.child {
let possible_table = self
.map
.entry(key.text)
.or_insert(TomlValue::Table(Table::default()));
let table = match possible_table {
TomlValue::Array(array) => {
let Some(TomlValue::Table(table)) = array.last_mut() else {
return true;
};
table
}
TomlValue::Table(table) => table,
_ => return true,
};
table.insert(*child, value)
} else {
self.map.insert(key.text, value).is_some()
}
}
/// Gets a value from the table, or inserts one if it doesn't exist. This handles dotted keys automatically,
/// but will return `None` if the key is invalid (ie indexes into something that isn't a table).
pub(crate) fn get_or_insert_mut(
&mut self,
key: Key<'a>,
value: TomlValue<'a>,
) -> Option<&mut TomlValue<'a>> {
if let Some(child) = key.child {
let possible_table = self
.map
.entry(key.text)
.or_insert(TomlValue::Table(Table::default()));
let table = match possible_table {
TomlValue::Array(array) => {
let Some(TomlValue::Table(table)) = array.last_mut() else {
return None;
};
table
}
TomlValue::Table(table) => table,
_ => return None,
};
table.get_or_insert_mut(*child, value)
} else {
Some(self.map.entry(key.text).or_insert(value))
}
}
/// Iterates over the (key, value) pairs in this table. This replaces the [`HashMap`]'s normal iter method,
/// so that the keys are normal `&str`s instead of boml's internal [`CowSpan`] string type.
pub fn iter(&self) -> impl Iterator<Item = (&str, &TomlValue<'_>)> {
self.map.iter().map(|(k, v)| (k.as_str(), v))
}
}
impl<'a> Deref for Table<'a> {
type Target = HashMap<CowSpan<'a>, TomlValue<'a>>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
/// Errors for the `get_<type>` methods in [`Table`].
#[derive(Debug, PartialEq)]
pub enum TomlGetError<'a, 'table> {
/// There was no value for the provided key.
InvalidKey,
/// The value for the provided key had a different type. Stores the
/// value for that key and its type.
TypeMismatch(&'a TomlValue<'table>, TomlValueType),
}