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