1#[cfg(feature = "typed")]
8use std::fmt::Debug;
9
10#[cfg(feature = "typed")]
11use serde::{Deserialize, Serialize};
12use toml::Value;
13
14use crate::error::{Error, Result};
15use crate::tokenizer::tokenize_with_seperator;
16
17pub trait TomlValueReadExt<'doc> {
19 fn read_with_seperator(&'doc self, query: &str, sep: char) -> Result<Option<&'doc Value>>;
22
23 fn read_mut_with_seperator(
26 &'doc mut self,
27 query: &str,
28 sep: char,
29 ) -> Result<Option<&'doc mut Value>>;
30
31 fn read(&'doc self, query: &str) -> Result<Option<&'doc Value>> {
33 self.read_with_seperator(query, '.')
34 }
35
36 fn read_mut(&'doc mut self, query: &str) -> Result<Option<&'doc mut Value>> {
38 self.read_mut_with_seperator(query, '.')
39 }
40
41 #[cfg(feature = "typed")]
42 fn read_deserialized<'de, D: Deserialize<'de>>(&'doc self, query: &str) -> Result<Option<D>> {
43 let raw = self.read(query)?;
44
45 match raw {
46 Some(value) => {
47 let deserialized = value.clone().try_into().map_err(Error::TomlDeserialize)?;
48 Ok(Some(deserialized))
49 }
50 None => Ok(None),
51 }
52 }
53
54 #[cfg(feature = "typed")]
55 fn read_partial<'a, P: Partial<'a>>(&'doc self) -> Result<Option<P::Output>> {
56 self.read_deserialized::<P::Output>(P::LOCATION)
57 }
58}
59
60#[cfg(feature = "typed")]
62pub trait Partial<'a> {
63 const LOCATION: &'static str;
65
66 type Output: Serialize + Deserialize<'a> + Debug;
68}
69
70impl<'doc> TomlValueReadExt<'doc> for Value {
71 fn read_with_seperator(&'doc self, query: &str, sep: char) -> Result<Option<&'doc Value>> {
72 use crate::resolver::non_mut_resolver::resolve;
73
74 tokenize_with_seperator(query, sep).and_then(move |tokens| resolve(self, &tokens, false))
75 }
76
77 fn read_mut_with_seperator(
78 &'doc mut self,
79 query: &str,
80 sep: char,
81 ) -> Result<Option<&'doc mut Value>> {
82 use crate::resolver::mut_resolver::resolve;
83
84 tokenize_with_seperator(query, sep).and_then(move |tokens| resolve(self, &tokens, false))
85 }
86}
87
88pub trait TomlValueReadTypeExt<'doc>: TomlValueReadExt<'doc> {
89 fn read_string(&'doc self, query: &str) -> Result<Option<String>>;
90 fn read_int(&'doc self, query: &str) -> Result<Option<i64>>;
91 fn read_float(&'doc self, query: &str) -> Result<Option<f64>>;
92 fn read_bool(&'doc self, query: &str) -> Result<Option<bool>>;
93}
94
95macro_rules! make_type_getter {
96 ($fnname:ident, $rettype:ty, $typename:expr, $matcher:pat => $implementation:expr) => {
97 fn $fnname(&'doc self, query: &str) -> Result<Option<$rettype>> {
98 self.read_with_seperator(query, '.').and_then(|o| match o {
99 $matcher => Ok(Some($implementation)),
100 Some(o) => Err(Error::TypeError($typename, crate::util::name_of_val(&o)).into()),
101 None => Ok(None),
102 })
103 }
104 };
105}
106
107impl<'doc, T> TomlValueReadTypeExt<'doc> for T
108where
109 T: TomlValueReadExt<'doc>,
110{
111 make_type_getter!(read_string, String, "String", Some(Value::String(ref obj)) => obj.clone());
112 make_type_getter!(read_int, i64, "Integer", Some(&Value::Integer(obj)) => obj);
113 make_type_getter!(read_float, f64, "Float", Some(&Value::Float(obj)) => obj);
114 make_type_getter!(read_bool, bool, "Boolean", Some(&Value::Boolean(obj)) => obj);
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120 use toml::from_str as toml_from_str;
121
122 #[test]
123 fn test_read_empty() {
124 let toml: Value = toml_from_str("").unwrap();
125
126 let val = toml.read_with_seperator(&String::from("a"), '.');
127
128 assert!(val.is_ok());
129 let val = val.unwrap();
130
131 assert!(val.is_none());
132 }
133
134 #[test]
135 fn test_read_table() {
136 let toml: Value = toml_from_str(
137 r#"
138 [table]
139 "#,
140 )
141 .unwrap();
142
143 let val = toml.read_with_seperator(&String::from("table"), '.');
144
145 assert!(val.is_ok());
146 let val = val.unwrap();
147
148 assert!(val.is_some());
149 let val = val.unwrap();
150
151 assert!(is_match!(val, &Value::Table(_)));
152 match val {
153 Value::Table(ref t) => assert!(t.is_empty()),
154 _ => panic!("What just happened?"),
155 }
156 }
157
158 #[test]
159 fn test_read_table_value() {
160 let toml: Value = toml_from_str(
161 r#"
162 [table]
163 a = 1
164 "#,
165 )
166 .unwrap();
167
168 let val = toml.read_with_seperator(&String::from("table.a"), '.');
169
170 assert!(val.is_ok());
171 let val = val.unwrap();
172
173 assert!(val.is_some());
174 let val = val.unwrap();
175
176 assert!(is_match!(val, &Value::Integer(1)));
177 }
178
179 #[test]
180 fn test_read_empty_table_value() {
181 let toml: Value = toml_from_str(
182 r#"
183 [table]
184 "#,
185 )
186 .unwrap();
187
188 let val = toml.read_with_seperator(&String::from("table.a"), '.');
189 assert!(val.is_ok());
190 let val = val.unwrap();
191
192 assert!(val.is_none());
193 }
194
195 #[test]
196 fn test_read_table_index() {
197 let toml: Value = toml_from_str(
198 r#"
199 [table]
200 "#,
201 )
202 .unwrap();
203
204 let val = toml.read_with_seperator(&String::from("table.[0]"), '.');
205 assert!(val.is_err());
206 let err = val.unwrap_err();
207
208 assert!(is_match!(err, Error::NoIndexInTable(_)));
209 }
210
211 #[test]
218 fn test_read_empty_without_seperator() {
219 let toml: Value = toml_from_str("").unwrap();
220
221 let val = toml.read(&String::from("a"));
222 assert!(val.is_ok());
223 let val = val.unwrap();
224
225 assert!(val.is_none());
226 }
227
228 #[test]
229 fn test_read_table_without_seperator() {
230 let toml: Value = toml_from_str(
231 r#"
232 [table]
233 "#,
234 )
235 .unwrap();
236
237 let val = toml.read(&String::from("table"));
238
239 assert!(val.is_ok());
240 let val = val.unwrap();
241
242 assert!(val.is_some());
243 let val = val.unwrap();
244
245 assert!(is_match!(val, &Value::Table(_)));
246 match val {
247 Value::Table(ref t) => assert!(t.is_empty()),
248 _ => panic!("What just happened?"),
249 }
250 }
251
252 #[test]
253 fn test_read_table_value_without_seperator() {
254 let toml: Value = toml_from_str(
255 r#"
256 [table]
257 a = 1
258 "#,
259 )
260 .unwrap();
261
262 let val = toml.read(&String::from("table.a"));
263
264 assert!(val.is_ok());
265 let val = val.unwrap();
266
267 assert!(val.is_some());
268 let val = val.unwrap();
269
270 assert!(is_match!(val, &Value::Integer(1)));
271 }
272
273 #[test]
274 fn test_read_empty_table_value_without_seperator() {
275 let toml: Value = toml_from_str(
276 r#"
277 [table]
278 "#,
279 )
280 .unwrap();
281
282 let val = toml.read(&String::from("table.a"));
283 assert!(val.is_ok());
284 let val = val.unwrap();
285
286 assert!(val.is_none());
287 }
288
289 #[test]
290 fn test_read_table_index_without_seperator() {
291 let toml: Value = toml_from_str(
292 r#"
293 [table]
294 "#,
295 )
296 .unwrap();
297
298 let val = toml.read(&String::from("table.[0]"));
299 assert!(val.is_err());
300 let err = val.unwrap_err();
301
302 assert!(is_match!(err, Error::NoIndexInTable(_)));
303 }
304}
305
306#[cfg(test)]
307mod high_level_fn_test {
308 use super::*;
309 use toml::from_str as toml_from_str;
310
311 #[test]
312 fn test_read_table_value() {
313 let toml: Value = toml_from_str(
314 r#"
315 [table]
316 a = 1
317 "#,
318 )
319 .unwrap();
320
321 let val = toml.read_int("table.a").unwrap();
322
323 assert_eq!(val.unwrap(), 1);
324 }
325
326 #[cfg(feature = "typed")]
327 #[test]
328 fn test_name() {
329 let toml: Value = toml_from_str(
330 r#"
331 [table]
332 a = 1
333 "#,
334 )
335 .unwrap();
336
337 let val: u32 = toml.read_deserialized("table.a").unwrap().unwrap();
338
339 assert_eq!(val, 1);
340 }
341
342 #[cfg(feature = "typed")]
343 #[test]
344 fn test_deser() {
345 use crate::insert::TomlValueInsertExt;
346 use crate::read::TomlValueReadExt;
347 use toml::map::Map;
348
349 #[derive(Serialize, Deserialize, Debug)]
350 struct Test {
351 a: u64,
352 s: String,
353 }
354
355 let mut toml = Value::Table(Map::new());
356 let test = Test {
357 a: 15,
358 s: String::from("Helloworld"),
359 };
360
361 assert!(toml
362 .insert_serialized("table.value", test)
363 .unwrap()
364 .is_none());
365 let _: Test = toml.read_deserialized("table.value").unwrap().unwrap();
366 }
367}
368
369#[cfg(all(test, feature = "typed"))]
370mod partial_tests {
371 use super::*;
372
373 use toml::map::Map;
374 use toml::Value;
375
376 #[derive(Debug, Deserialize, Serialize)]
377 struct TestObj {
378 pub value: String,
379 }
380
381 impl<'a> Partial<'a> for TestObj {
382 const LOCATION: &'static str = "foo";
383 type Output = Self;
384 }
385
386 #[test]
387 fn test_compiles() {
388 let tbl = {
389 let mut tbl = Map::new();
390 tbl.insert(String::from("foo"), {
391 let mut tbl = Map::new();
392 tbl.insert(String::from("value"), Value::String(String::from("foobar")));
393 Value::Table(tbl)
394 });
395 Value::Table(tbl)
396 };
397
398 let obj: TestObj = tbl.read_partial::<TestObj>().unwrap().unwrap();
399 assert_eq!(obj.value, "foobar");
400 }
401}