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