1use {
4 crate::{
5 text::{CowSpan, Text},
6 types::{TomlValue, TomlValueType},
7 TomlError, TomlErrorKind,
8 },
9 std::{
10 collections::{
11 hash_map::{Entry, VacantEntry},
12 HashMap,
13 },
14 ops::Deref,
15 },
16};
17
18#[derive(Debug, PartialEq, Default)]
20pub struct TomlTable<'a> {
21 pub(crate) map: HashMap<CowSpan<'a>, TomlValue<'a>>,
22}
23impl<'a> TomlTable<'a> {
24 pub fn get_table(&self, key: &str) -> Result<&Self, TomlGetError<'_, 'a>> {
26 match self.get(key) {
27 None => Err(TomlGetError::InvalidKey),
28 Some(ref val) => {
29 if let TomlValue::Table(table) = val {
30 Ok(table)
31 } else {
32 Err(TomlGetError::TypeMismatch(val, val.ty()))
33 }
34 }
35 }
36 }
37 pub fn get_string(&self, key: &str) -> Result<&str, TomlGetError<'_, 'a>> {
39 match self.get(key) {
40 None => Err(TomlGetError::InvalidKey),
41 Some(ref val) => match val {
42 TomlValue::String(string) => Ok(string.as_str()),
43 other_val => Err(TomlGetError::TypeMismatch(other_val, other_val.ty())),
44 },
45 }
46 }
47 pub fn get_integer(&self, key: &str) -> Result<i64, TomlGetError<'_, 'a>> {
49 match self.get(key) {
50 None => Err(TomlGetError::InvalidKey),
51 Some(ref val) => {
52 if let TomlValue::Integer(int) = val {
53 Ok(*int)
54 } else {
55 Err(TomlGetError::TypeMismatch(val, val.ty()))
56 }
57 }
58 }
59 }
60 pub fn get_float(&self, key: &str) -> Result<f64, TomlGetError<'_, 'a>> {
62 match self.get(key) {
63 None => Err(TomlGetError::InvalidKey),
64 Some(ref val) => {
65 if let TomlValue::Float(float) = val {
66 Ok(*float)
67 } else {
68 Err(TomlGetError::TypeMismatch(val, val.ty()))
69 }
70 }
71 }
72 }
73 pub fn get_boolean(&self, key: &str) -> Result<bool, TomlGetError<'_, 'a>> {
75 match self.get(key) {
76 None => Err(TomlGetError::InvalidKey),
77 Some(ref val) => {
78 if let TomlValue::Boolean(bool) = val {
79 Ok(*bool)
80 } else {
81 Err(TomlGetError::TypeMismatch(val, val.ty()))
82 }
83 }
84 }
85 }
86 pub fn get_array(&self, key: &str) -> Result<&Vec<TomlValue<'a>>, TomlGetError<'_, 'a>> {
88 match self.get(key) {
89 None => Err(TomlGetError::InvalidKey),
90 Some(ref val) => {
91 if let TomlValue::Array(array, _) = val {
92 Ok(array)
93 } else {
94 Err(TomlGetError::TypeMismatch(val, val.ty()))
95 }
96 }
97 }
98 }
99
100 pub(crate) fn value_entry<'b>(
101 &'b mut self,
102 text: &mut Text<'a>,
103 ) -> Result<VacantEntry<'b, CowSpan<'a>, TomlValue<'a>>, TomlError<'a>> {
104 let start = text.idx();
105 let (table, key) = crate::parser::key::parse_nested(text, self)?;
106
107 match table.map.entry(key) {
108 Entry::Occupied(_) => Err(TomlError {
109 src: text.excerpt_to_idx(start..),
110 kind: TomlErrorKind::ReusedKey,
111 }),
112 Entry::Vacant(vacant) => Ok(vacant),
113 }
114 }
115}
116impl<'a> Deref for TomlTable<'a> {
117 type Target = HashMap<CowSpan<'a>, TomlValue<'a>>;
118
119 fn deref(&self) -> &Self::Target {
120 &self.map
121 }
122}
123
124#[derive(Debug, PartialEq)]
126pub enum TomlGetError<'a, 'table> {
127 InvalidKey,
129 TypeMismatch(&'a TomlValue<'table>, TomlValueType),
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 struct Tester {
139 key: &'static str,
140 value: TomlValue<'static>,
141 }
142 impl Tester {
143 fn build(self) -> TomlTable<'static> {
144 println!("Running test for key `{}`", self.key);
145
146 let mut table = TomlTable::default();
147 table
148 .value_entry(&mut Text::new(self.key))
149 .unwrap()
150 .insert(self.value);
151 table
152 }
153 }
154
155 #[test]
156 fn test_table_keys() {
157 let basic = Tester {
158 key: "bool",
159 value: TomlValue::Boolean(true),
160 }
161 .build();
162 assert_eq!(basic.get("bool"), Some(&TomlValue::Boolean(true)));
163
164 let dotted = Tester {
165 key: "dot.bool",
166 value: TomlValue::Boolean(true),
167 }
168 .build();
169 let Some(TomlValue::Table(subtable)) = dotted.get("dot") else {
170 panic!()
171 };
172 assert_eq!(subtable.get("bool"), Some(&TomlValue::Boolean(true)));
173
174 let quoted = Tester {
175 key: "'wowza.hi'",
176 value: TomlValue::Boolean(true),
177 }
178 .build();
179 assert_eq!(quoted.get("wowza.hi"), Some(&TomlValue::Boolean(true)));
180
181 let quoted_alt = Tester {
182 key: r#""wowza.hi""#,
183 value: TomlValue::Boolean(true),
184 }
185 .build();
186 assert_eq!(quoted_alt.get("wowza.hi"), Some(&TomlValue::Boolean(true)));
187 }
188}