use {
crate::{
TomlError, TomlErrorKind,
text::{CowSpan, Text},
types::{TomlArray, TomlValue, TomlValueType},
},
std::{
collections::{
HashMap,
hash_map::{Entry, VacantEntry},
},
ops::Deref,
},
};
#[derive(Debug, PartialEq, Default)]
pub struct TomlTable<'a> {
pub(crate) map: HashMap<CowSpan<'a>, TomlValue<'a>>,
pub(crate) defined: bool,
}
impl<'a> TomlTable<'a> {
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.ty()))
}
}
}
}
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.ty())),
},
}
}
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.ty()))
}
}
}
}
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.ty()))
}
}
}
}
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.ty()))
}
}
}
}
pub fn get_array(&self, key: &str) -> Result<&TomlArray<'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.ty()))
}
}
}
}
pub(crate) fn value_entry<'b>(
&'b mut self,
text: &mut Text<'a>,
) -> Result<VacantEntry<'b, CowSpan<'a>, TomlValue<'a>>, TomlError<'a>> {
let start = text.idx();
let (table, key) = crate::parser::key::parse_nested(text, self)?;
match table.map.entry(key) {
Entry::Occupied(_) => Err(TomlError {
src: text.excerpt_to_idx(start..),
kind: TomlErrorKind::ReusedKey,
}),
Entry::Vacant(vacant) => Ok(vacant),
}
}
}
impl<'a> Deref for TomlTable<'a> {
type Target = HashMap<CowSpan<'a>, TomlValue<'a>>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
#[derive(Debug, PartialEq)]
pub enum TomlGetError<'a, 'table> {
InvalidKey,
TypeMismatch(&'a TomlValue<'table>, TomlValueType),
}
#[cfg(test)]
mod tests {
use super::*;
struct Tester {
key: &'static str,
value: TomlValue<'static>,
}
impl Tester {
fn build(self) -> TomlTable<'static> {
println!("Running test for key `{}`", self.key);
let mut table = TomlTable::default();
table
.value_entry(&mut Text::new(self.key))
.unwrap()
.insert(self.value);
table
}
}
#[test]
fn test_table_keys() {
let basic = Tester {
key: "bool",
value: TomlValue::Boolean(true),
}
.build();
assert_eq!(basic.get("bool"), Some(&TomlValue::Boolean(true)));
let dotted = Tester {
key: "dot.bool",
value: TomlValue::Boolean(true),
}
.build();
let Some(TomlValue::Table(subtable)) = dotted.get("dot") else {
panic!()
};
assert_eq!(subtable.get("bool"), Some(&TomlValue::Boolean(true)));
let quoted = Tester {
key: "'wowza.hi'",
value: TomlValue::Boolean(true),
}
.build();
assert_eq!(quoted.get("wowza.hi"), Some(&TomlValue::Boolean(true)));
let quoted_alt = Tester {
key: r#""wowza.hi""#,
value: TomlValue::Boolean(true),
}
.build();
assert_eq!(quoted_alt.get("wowza.hi"), Some(&TomlValue::Boolean(true)));
}
}