1#[cfg(feature = "typed")]
4use serde::Serialize;
5use toml::Value;
6
7use crate::error::{Error, Result};
8use crate::tokenizer::tokenize_with_seperator;
9use crate::tokenizer::Token;
10
11pub trait TomlValueSetExt {
12 fn set_with_seperator(&mut self, query: &str, sep: char, value: Value)
31 -> Result<Option<Value>>;
32
33 fn set(&mut self, query: &str, value: Value) -> Result<Option<Value>> {
37 self.set_with_seperator(query, '.', value)
38 }
39
40 #[cfg(feature = "typed")]
42 fn set_serialized<S: Serialize>(&mut self, query: &str, value: S) -> Result<Option<Value>> {
43 let value = Value::try_from(value).map_err(Error::TomlSerialize)?;
44 self.set(query, value)
45 }
46}
47
48impl TomlValueSetExt for Value {
49 fn set_with_seperator(
50 &mut self,
51 query: &str,
52 sep: char,
53 value: Value,
54 ) -> Result<Option<Value>> {
55 use crate::resolver::mut_resolver::resolve;
56
57 let mut tokens = tokenize_with_seperator(query, sep)?;
58 let last = tokens.pop_last();
59
60 let val = resolve(self, &tokens, true)?.unwrap(); let last = last.unwrap_or_else(|| Box::new(tokens));
62
63 match *last {
64 Token::Identifier { ident, .. } => match val {
65 Value::Table(ref mut t) => Ok(t.insert(ident, value)),
66 Value::Array(_) => Err(Error::NoIdentifierInArray(ident)),
67 _ => Err(Error::QueryingValueAsTable(ident)),
68 },
69
70 Token::Index { idx, .. } => match val {
71 Value::Array(ref mut a) => {
72 if a.len() > idx {
73 let result = a.swap_remove(idx);
74 a.insert(idx, value);
75 Ok(Some(result))
76 } else {
77 a.push(value);
78 Ok(None)
79 }
80 }
81 Value::Table(_) => Err(Error::NoIndexInTable(idx)),
82 _ => Err(Error::QueryingValueAsArray(idx)),
83 },
84 }
85 }
86}
87
88#[cfg(test)]
89mod test {
90 use super::*;
91 use toml::from_str as toml_from_str;
92 use toml::Value;
93
94 #[test]
95 fn test_set_with_seperator_into_table() {
96 let mut toml: Value = toml_from_str(
97 r#"
98 [table]
99 a = 0
100 "#,
101 )
102 .unwrap();
103
104 let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));
105
106 assert!(res.is_ok());
107
108 let res = res.unwrap();
109 assert!(res.is_some());
110 let res = res.unwrap();
111 assert!(is_match!(res, Value::Integer(0)));
112
113 assert!(is_match!(toml, Value::Table(_)));
114 match toml {
115 Value::Table(ref t) => {
116 assert!(!t.is_empty());
117
118 let inner = t.get("table");
119 assert!(inner.is_some());
120
121 let inner = inner.unwrap();
122 assert!(is_match!(inner, Value::Table(_)));
123 match inner {
124 Value::Table(ref t) => {
125 assert!(!t.is_empty());
126
127 let a = t.get("a");
128 assert!(a.is_some());
129
130 let a = a.unwrap();
131 assert!(is_match!(a, Value::Integer(1)));
132 }
133 _ => panic!("What just happenend?"),
134 }
135 }
136 _ => panic!("What just happenend?"),
137 }
138 }
139
140 #[test]
141 fn test_set_with_seperator_into_table_key_nonexistent() {
142 let mut toml: Value = toml_from_str(
143 r#"
144 [table]
145 "#,
146 )
147 .unwrap();
148
149 let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));
150
151 assert!(res.is_ok());
152 let res = res.unwrap();
153
154 assert!(res.is_none());
155
156 assert!(is_match!(toml, Value::Table(_)));
157 match toml {
158 Value::Table(ref t) => {
159 assert!(!t.is_empty());
160
161 let inner = t.get("table");
162 assert!(inner.is_some());
163
164 let inner = inner.unwrap();
165 assert!(is_match!(inner, Value::Table(_)));
166 match inner {
167 Value::Table(ref t) => {
168 assert!(!t.is_empty());
169
170 let a = t.get("a");
171 assert!(a.is_some());
172
173 let a = a.unwrap();
174 assert!(is_match!(a, Value::Integer(1)));
175 }
176 _ => panic!("What just happenend?"),
177 }
178 }
179 _ => panic!("What just happenend?"),
180 }
181 }
182
183 #[test]
184 fn test_set_with_seperator_into_array() {
185 use std::ops::Index;
186
187 let mut toml: Value = toml_from_str(
188 r#"
189 array = [ 0 ]
190 "#,
191 )
192 .unwrap();
193
194 let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));
195
196 assert!(res.is_ok());
197
198 let res = res.unwrap();
199 assert!(res.is_some());
200 let res = res.unwrap();
201 assert!(is_match!(res, Value::Integer(0)));
202
203 assert!(is_match!(toml, Value::Table(_)));
204 match toml {
205 Value::Table(ref t) => {
206 assert!(!t.is_empty());
207
208 let inner = t.get("array");
209 assert!(inner.is_some());
210
211 let inner = inner.unwrap();
212 assert!(is_match!(inner, Value::Array(_)));
213 match inner {
214 Value::Array(ref a) => {
215 assert!(!a.is_empty());
216 assert!(is_match!(a.index(0), Value::Integer(1)));
217 }
218 _ => panic!("What just happenend?"),
219 }
220 }
221 _ => panic!("What just happenend?"),
222 }
223 }
224
225 #[test]
226 fn test_set_with_seperator_into_table_index_nonexistent() {
227 use std::ops::Index;
228
229 let mut toml: Value = toml_from_str(
230 r#"
231 array = []
232 "#,
233 )
234 .unwrap();
235
236 let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));
237
238 assert!(res.is_ok());
239
240 let res = res.unwrap();
241 assert!(res.is_none());
242
243 assert!(is_match!(toml, Value::Table(_)));
244 match toml {
245 Value::Table(ref t) => {
246 assert!(!t.is_empty());
247
248 let inner = t.get("array");
249 assert!(inner.is_some());
250
251 let inner = inner.unwrap();
252 assert!(is_match!(inner, Value::Array(_)));
253 match inner {
254 Value::Array(ref a) => {
255 assert!(!a.is_empty());
256 assert!(is_match!(a.index(0), Value::Integer(1)));
257 }
258 _ => panic!("What just happenend?"),
259 }
260 }
261 _ => panic!("What just happenend?"),
262 }
263 }
264
265 #[test]
266 #[allow(clippy::cognitive_complexity)]
267 fn test_set_with_seperator_into_nested_table() {
268 let mut toml: Value = toml_from_str(
269 r#"
270 [a.b.c]
271 d = 0
272 "#,
273 )
274 .unwrap();
275
276 let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));
277
278 assert!(res.is_ok());
279
280 let res = res.unwrap();
281 assert!(res.is_some());
282 let res = res.unwrap();
283 assert!(is_match!(res, Value::Integer(0)));
284
285 assert!(is_match!(toml, Value::Table(_)));
286 match toml {
287 Value::Table(ref t) => {
288 assert!(!t.is_empty());
289
290 let a = t.get("a");
291 assert!(a.is_some());
292
293 let a = a.unwrap();
294 assert!(is_match!(a, Value::Table(_)));
295 match a {
296 Value::Table(ref a) => {
297 assert!(!a.is_empty());
298
299 let b_tab = a.get("b");
300 assert!(b_tab.is_some());
301
302 let b_tab = b_tab.unwrap();
303 assert!(is_match!(b_tab, Value::Table(_)));
304 match b_tab {
305 Value::Table(ref b) => {
306 assert!(!b.is_empty());
307
308 let c_tab = b.get("c");
309 assert!(c_tab.is_some());
310
311 let c_tab = c_tab.unwrap();
312 assert!(is_match!(c_tab, Value::Table(_)));
313 match c_tab {
314 Value::Table(ref c) => {
315 assert!(!c.is_empty());
316
317 let d = c.get("d");
318 assert!(d.is_some());
319
320 let d = d.unwrap();
321 assert!(is_match!(d, Value::Integer(1)));
322 }
323 _ => panic!("What just happenend?"),
324 }
325 }
326 _ => panic!("What just happenend?"),
327 }
328 }
329 _ => panic!("What just happenend?"),
330 }
331 }
332 _ => panic!("What just happenend?"),
333 }
334 }
335
336 #[test]
337 fn test_set_with_seperator_into_nonexistent_table() {
338 let mut toml: Value = toml_from_str("").unwrap();
339
340 let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));
341
342 assert!(res.is_err());
343
344 let res = res.unwrap_err();
345 assert!(is_match!(res, Error::IdentifierNotFoundInDocument(_)));
346 }
347
348 #[test]
349 fn test_set_with_seperator_into_nonexistent_array() {
350 let mut toml: Value = toml_from_str("").unwrap();
351
352 let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));
353
354 assert!(res.is_err());
355
356 let res = res.unwrap_err();
357 assert!(is_match!(res, Error::NoIndexInTable(0)));
358 }
359
360 #[test]
361 fn test_set_with_seperator_ident_into_ary() {
362 let mut toml: Value = toml_from_str(
363 r#"
364 array = [ 0 ]
365 "#,
366 )
367 .unwrap();
368
369 let res = toml.set_with_seperator(&String::from("array.foo"), '.', Value::Integer(2));
370
371 assert!(res.is_err());
372 let res = res.unwrap_err();
373
374 assert!(is_match!(res, Error::NoIdentifierInArray(_)));
375 }
376
377 #[test]
378 fn test_set_with_seperator_index_into_table() {
379 let mut toml: Value = toml_from_str(
380 r#"
381 foo = { bar = 1 }
382 "#,
383 )
384 .unwrap();
385
386 let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));
387
388 assert!(res.is_err());
389 let res = res.unwrap_err();
390
391 assert!(is_match!(res, Error::NoIndexInTable(_)));
392 }
393
394 #[test]
395 fn test_set_with_seperator_ident_into_non_structure() {
396 let mut toml: Value = toml_from_str(
397 r#"
398 val = 0
399 "#,
400 )
401 .unwrap();
402
403 let res = toml.set_with_seperator(&String::from("val.foo"), '.', Value::Integer(2));
404
405 assert!(res.is_err());
406 let res = res.unwrap_err();
407
408 assert!(is_match!(res, Error::QueryingValueAsTable(_)));
409 }
410
411 #[test]
412 fn test_set_with_seperator_index_into_non_structure() {
413 let mut toml: Value = toml_from_str(
414 r#"
415 foo = 1
416 "#,
417 )
418 .unwrap();
419
420 let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));
421
422 assert!(res.is_err());
423 let res = res.unwrap_err();
424
425 assert!(is_match!(res, Error::QueryingValueAsArray(_)));
426 }
427
428 #[cfg(feature = "typed")]
429 #[test]
430 fn test_serialize() {
431 use crate::insert::TomlValueInsertExt;
432 use toml::map::Map;
433
434 #[derive(Serialize, Deserialize, Debug)]
435 struct Test {
436 a: u64,
437 s: String,
438 }
439
440 let mut toml = Value::Table(Map::new());
441 let test = Test {
442 a: 15,
443 s: String::from("Helloworld"),
444 };
445
446 assert!(toml
447 .insert_serialized("table.value", test)
448 .unwrap()
449 .is_none());
450
451 eprintln!("{:#}", toml);
452
453 match toml {
454 Value::Table(ref tab) => match tab.get("table").unwrap() {
455 Value::Table(ref inner) => match inner.get("value").unwrap() {
456 Value::Table(ref data) => {
457 assert!(is_match!(data.get("a").unwrap(), Value::Integer(15)));
458 match data.get("s").unwrap() {
459 Value::String(ref s) => assert_eq!(s, "Helloworld"),
460 _ => unreachable!(),
461 };
462 }
463 _ => unreachable!(),
464 },
465 _ => unreachable!(),
466 },
467 _ => unreachable!(),
468 }
469 }
470}