jsonptr/
assign.rs

1//! # Assign values based on JSON [`Pointer`]s
2//!
3//! This module provides the [`Assign`] trait which allows for the assignment of
4//! values based on a JSON Pointer.
5//!
6//! This module is enabled by default with the `"assign"` feature flag.
7//!
8//! # Expansion
9//! The path will automatically be expanded if the [`Pointer`] is not fully
10//! exhausted before reaching a non-existent key in the case of objects, index
11//! in the case of arrays, or a scalar value (including `null`) based upon a
12//! best-guess effort on the meaning of each [`Token`](crate::Token):
13//! - If the [`Token`](crate::Token) is equal to `"0"` or `"-"`, the token will
14//!   be considered an index of an array.
15//! - All tokens not equal to `"0"` or `"-"` will be considered keys of an
16//!   object.
17//!
18//! ## Usage
19//! [`Assign`] can be used directly or through the [`assign`](Pointer::assign)
20//! method of [`Pointer`].
21//!
22//! ```rust
23//! use jsonptr::Pointer;
24//! use serde_json::json;
25//! let mut data = json!({"foo": "bar"});
26//! let ptr = Pointer::from_static("/foo");
27//! let replaced = ptr.assign(&mut data, "baz").unwrap();
28//! assert_eq!(replaced, Some(json!("bar")));
29//! assert_eq!(data, json!({"foo": "baz"}));
30//! ```
31//! ## Provided implementations
32//!
33//! | Lang  |     value type      | feature flag | Default |
34//! | ----- |: ----------------- :|: ---------- :| ------- |
35//! | JSON  | `serde_json::Value` |   `"json"`   |   ✓     |
36//! | TOML  |    `toml::Value`    |   `"toml"`   |         |
37//!
38
39use crate::{
40    index::{OutOfBoundsError, ParseIndexError},
41    Pointer,
42};
43use core::fmt::{self, Debug};
44
45/*
46░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
47╔══════════════════════════════════════════════════════════════════════════════╗
48║                                                                              ║
49║                                    Assign                                    ║
50║                                   ¯¯¯¯¯¯¯¯                                   ║
51╚══════════════════════════════════════════════════════════════════════════════╝
52░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
53*/
54
55/// Implemented by types which can internally assign a
56/// ([`Value`](`Assign::Value`)) at a path represented by a JSON [`Pointer`].
57///
58/// ## Expansion
59/// For provided implementations (`"json"`, and `"toml"`) path will
60/// automatically be expanded the if the [`Pointer`] is not fully exhausted
61/// before reaching a non-existent key in the case of objects, index in the case
62/// of arrays, or a scalar value (including `null`) based upon a best-guess
63/// effort on the meaning of each [`Token`](crate::Token):
64///
65/// - If the [`Token`](crate::Token) is equal to `"0"` or `"-"`, the token will
66///   be considered an index of an array.
67/// - All tokens not equal to `"0"` or `"-"` will be considered keys of an
68///   object.
69///
70/// ## Examples
71///
72/// ### Successful assignment with replacement
73/// This example demonstrates a successful assignment with replacement.
74/// ```rust
75/// use jsonptr::{Pointer, assign::Assign};
76/// use serde_json::{json, Value};
77///
78/// let mut data = json!({"foo": "bar"});
79/// let ptr = Pointer::from_static("/foo");
80///
81/// let replaced = data.assign(&ptr, "baz").unwrap();
82/// assert_eq!(replaced, Some(json!("bar")));
83/// assert_eq!(data, json!({"foo": "baz"}));
84/// ```
85///
86/// ### Successful assignment with path expansion
87/// This example demonstrates path expansion, including an array index (`"0"`)
88/// ```rust
89/// # use jsonptr::{Pointer, assign::Assign};
90/// # use serde_json::{json, Value};
91/// let ptr = Pointer::from_static("/foo/bar/0/baz");
92/// let mut data = serde_json::json!({"foo": "bar"});
93///
94/// let replaced = data.assign(ptr, json!("qux")).unwrap();
95///
96/// assert_eq!(&data, &json!({"foo": {"bar": [{"baz": "qux"}]}}));
97/// assert_eq!(replaced, Some(json!("bar")));
98/// ```
99///
100/// ### Successful assignment with `"-"` token
101///
102/// This example performs path expansion using the special `"-"` token (per RFC
103/// 6901) to represent the next element in an array.
104///
105/// ```rust
106/// # use jsonptr::{Pointer, assign::Assign};
107/// # use serde_json::{json, Value};
108/// let ptr = Pointer::from_static("/foo/bar/-/baz");
109/// let mut data = json!({"foo": "bar"});
110///
111/// let replaced = data.assign(ptr, json!("qux")).unwrap();
112/// assert_eq!(&data, &json!({"foo": {"bar": [{"baz": "qux"}]}}));
113/// assert_eq!(replaced, Some(json!("bar")));
114/// ```
115pub trait Assign {
116    /// The type of value that this implementation can operate on.
117    type Value;
118
119    /// Error associated with `Assign`
120    type Error;
121
122    /// Assigns a value of based on the path provided by a JSON Pointer,
123    /// returning the replaced value, if any.
124    ///
125    /// # Errors
126    /// Returns [`Self::Error`] if the assignment fails.
127    fn assign<V>(&mut self, ptr: &Pointer, value: V) -> Result<Option<Self::Value>, Self::Error>
128    where
129        V: Into<Self::Value>;
130}
131
132/*
133░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
134╔══════════════════════════════════════════════════════════════════════════════╗
135║                                                                              ║
136║                                 AssignError                                  ║
137║                                ¯¯¯¯¯¯¯¯¯¯¯¯¯                                 ║
138╚══════════════════════════════════════════════════════════════════════════════╝
139░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
140*/
141
142/// Possible error returned from [`Assign`] implementations for
143/// [`serde_json::Value`] and
144/// [`toml::Value`](https://docs.rs/toml/0.8.14/toml/index.html).
145#[derive(Debug, PartialEq, Eq)]
146pub enum AssignError {
147    /// A `Token` within the `Pointer` failed to be parsed as an array index.
148    FailedToParseIndex {
149        /// Offset of the partial pointer starting with the invalid index.
150        offset: usize,
151        /// The source [`ParseIndexError`]
152        source: ParseIndexError,
153    },
154
155    /// target array.
156    OutOfBounds {
157        /// Offset of the partial pointer starting with the invalid index.
158        offset: usize,
159        /// The source [`OutOfBoundsError`]
160        source: OutOfBoundsError,
161    },
162}
163
164impl fmt::Display for AssignError {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        match self {
167            Self::FailedToParseIndex { offset, .. } => {
168                write!(
169                    f,
170                    "assignment failed due to an invalid index at offset {offset}"
171                )
172            }
173            Self::OutOfBounds { offset, .. } => {
174                write!(
175                    f,
176                    "assignment failed due to index at offset {offset} being out of bounds"
177                )
178            }
179        }
180    }
181}
182
183#[cfg(feature = "std")]
184impl std::error::Error for AssignError {
185    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
186        match self {
187            Self::FailedToParseIndex { source, .. } => Some(source),
188            Self::OutOfBounds { source, .. } => Some(source),
189        }
190    }
191}
192
193enum Assigned<'v, V> {
194    Done(Option<V>),
195    Continue { next_dest: &'v mut V, same_value: V },
196}
197
198/*
199░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
200╔══════════════════════════════════════════════════════════════════════════════╗
201║                                                                              ║
202║                                  json impl                                   ║
203║                                 ¯¯¯¯¯¯¯¯¯¯¯                                  ║
204╚══════════════════════════════════════════════════════════════════════════════╝
205░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
206*/
207
208#[cfg(feature = "json")]
209mod json {
210    use super::{Assign, AssignError, Assigned};
211    use crate::{Pointer, Token};
212    use alloc::{
213        string::{String, ToString},
214        vec::Vec,
215    };
216
217    use core::mem;
218    use serde_json::{map::Entry, Map, Value};
219
220    fn expand(mut remaining: &Pointer, mut value: Value) -> Value {
221        while let Some((ptr, tok)) = remaining.split_back() {
222            remaining = ptr;
223            match tok.encoded() {
224                "0" | "-" => {
225                    value = Value::Array(vec![value]);
226                }
227                _ => {
228                    let mut obj = Map::new();
229                    obj.insert(tok.to_string(), value);
230                    value = Value::Object(obj);
231                }
232            }
233        }
234        value
235    }
236    impl Assign for Value {
237        type Value = Value;
238        type Error = AssignError;
239        fn assign<V>(&mut self, ptr: &Pointer, value: V) -> Result<Option<Self::Value>, Self::Error>
240        where
241            V: Into<Self::Value>,
242        {
243            assign_value(ptr, self, value.into())
244        }
245    }
246
247    pub(crate) fn assign_value(
248        mut ptr: &Pointer,
249        mut dest: &mut Value,
250        mut value: Value,
251    ) -> Result<Option<Value>, AssignError> {
252        let mut offset = 0;
253
254        while let Some((token, tail)) = ptr.split_front() {
255            let tok_len = token.encoded().len();
256
257            let assigned = match dest {
258                Value::Array(array) => assign_array(token, tail, array, value, offset)?,
259                Value::Object(obj) => assign_object(token, tail, obj, value),
260                _ => assign_scalar(ptr, dest, value),
261            };
262            match assigned {
263                Assigned::Done(assignment) => {
264                    return Ok(assignment);
265                }
266                Assigned::Continue {
267                    next_dest: next_value,
268                    same_value: same_src,
269                } => {
270                    value = same_src;
271                    dest = next_value;
272                    ptr = tail;
273                }
274            }
275            offset += 1 + tok_len;
276        }
277
278        // Pointer is root, we can replace `dest` directly
279        let replaced = Some(core::mem::replace(dest, value));
280        Ok(replaced)
281    }
282    #[allow(clippy::needless_pass_by_value)]
283    fn assign_array<'v>(
284        token: Token<'_>,
285        remaining: &Pointer,
286        array: &'v mut Vec<Value>,
287        src: Value,
288        offset: usize,
289    ) -> Result<Assigned<'v, Value>, AssignError> {
290        // parsing the index
291        let idx = token
292            .to_index()
293            .map_err(|source| AssignError::FailedToParseIndex { offset, source })?
294            .for_len_incl(array.len())
295            .map_err(|source| AssignError::OutOfBounds { offset, source })?;
296
297        debug_assert!(idx <= array.len());
298
299        if idx < array.len() {
300            // element exists in the array, we either need to replace it or continue
301            // depending on whether this is the last token or not
302            if remaining.is_root() {
303                // last token, we replace the value and call it a day
304                Ok(Assigned::Done(Some(mem::replace(&mut array[idx], src))))
305            } else {
306                // not the last token, we continue with a mut ref to the element as
307                // the next value
308                Ok(Assigned::Continue {
309                    next_dest: &mut array[idx],
310                    same_value: src,
311                })
312            }
313        } else {
314            // element does not exist in the array.
315            // we create the path and assign the value
316            let src = expand(remaining, src);
317            array.push(src);
318            Ok(Assigned::Done(None))
319        }
320    }
321
322    #[allow(clippy::needless_pass_by_value)]
323    fn assign_object<'v>(
324        token: Token<'_>,
325        remaining: &Pointer,
326        obj: &'v mut Map<String, Value>,
327        src: Value,
328    ) -> Assigned<'v, Value> {
329        // grabbing the entry of the token
330        let entry = obj.entry(token.to_string());
331        // adding token to the pointer buf
332
333        match entry {
334            Entry::Occupied(entry) => {
335                // if the entry exists, we either replace it or continue
336                let entry = entry.into_mut();
337                if remaining.is_root() {
338                    // if this is the last token, we are done
339                    // grab the old value and replace it with the new one
340                    Assigned::Done(Some(mem::replace(entry, src)))
341                } else {
342                    // if this is not the last token, we continue with a mutable
343                    // reference to the entry as the next value
344                    Assigned::Continue {
345                        same_value: src,
346                        next_dest: entry,
347                    }
348                }
349            }
350            Entry::Vacant(entry) => {
351                // if the entry does not exist, we create a value based on the
352                // remaining path with the src value as a leaf and assign it to the
353                // entry
354                entry.insert(expand(remaining, src));
355                Assigned::Done(None)
356            }
357        }
358    }
359
360    fn assign_scalar<'v>(
361        remaining: &Pointer,
362        scalar: &'v mut Value,
363        value: Value,
364    ) -> Assigned<'v, Value> {
365        // scalar values are always replaced at the current buf (with its token)
366        // build the new src and we replace the value with it.
367        let replaced = Some(mem::replace(scalar, expand(remaining, value)));
368        Assigned::Done(replaced)
369    }
370}
371
372/*
373░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
374╔══════════════════════════════════════════════════════════════════════════════╗
375║                                                                              ║
376║                                  toml impl                                   ║
377║                                 ¯¯¯¯¯¯¯¯¯¯¯                                  ║
378╚══════════════════════════════════════════════════════════════════════════════╝
379░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
380*/
381
382#[cfg(feature = "toml")]
383mod toml {
384    use super::{Assign, AssignError, Assigned};
385    use crate::{Pointer, Token};
386    use alloc::{string::String, vec, vec::Vec};
387    use core::mem;
388    use toml::{map::Entry, map::Map, Value};
389
390    fn expand(mut remaining: &Pointer, mut value: Value) -> Value {
391        while let Some((ptr, tok)) = remaining.split_back() {
392            remaining = ptr;
393            match tok.encoded() {
394                "0" | "-" => {
395                    value = Value::Array(vec![value]);
396                }
397                _ => {
398                    let mut obj = Map::new();
399                    obj.insert(tok.to_string(), value);
400                    value = Value::Table(obj);
401                }
402            }
403        }
404        value
405    }
406
407    impl Assign for Value {
408        type Value = Value;
409        type Error = AssignError;
410        fn assign<V>(&mut self, ptr: &Pointer, value: V) -> Result<Option<Self::Value>, Self::Error>
411        where
412            V: Into<Self::Value>,
413        {
414            assign_value(ptr, self, value.into())
415        }
416    }
417
418    pub(crate) fn assign_value(
419        mut ptr: &Pointer,
420        mut dest: &mut Value,
421        mut value: Value,
422    ) -> Result<Option<Value>, AssignError> {
423        let mut offset = 0;
424
425        while let Some((token, tail)) = ptr.split_front() {
426            let tok_len = token.encoded().len();
427
428            let assigned = match dest {
429                Value::Array(array) => assign_array(token, tail, array, value, offset)?,
430                Value::Table(tbl) => assign_object(token, tail, tbl, value),
431                _ => assign_scalar(ptr, dest, value),
432            };
433            match assigned {
434                Assigned::Done(assignment) => {
435                    return Ok(assignment);
436                }
437                Assigned::Continue {
438                    next_dest: next_value,
439                    same_value: same_src,
440                } => {
441                    value = same_src;
442                    dest = next_value;
443                    ptr = tail;
444                }
445            }
446            offset += 1 + tok_len;
447        }
448
449        // Pointer is root, we can replace `dest` directly
450        let replaced = Some(mem::replace(dest, value));
451        Ok(replaced)
452    }
453
454    #[allow(clippy::needless_pass_by_value)]
455    fn assign_array<'v>(
456        token: Token<'_>,
457        remaining: &Pointer,
458        array: &'v mut Vec<Value>,
459        src: Value,
460        offset: usize,
461    ) -> Result<Assigned<'v, Value>, AssignError> {
462        // parsing the index
463        let idx = token
464            .to_index()
465            .map_err(|source| AssignError::FailedToParseIndex { offset, source })?
466            .for_len_incl(array.len())
467            .map_err(|source| AssignError::OutOfBounds { offset, source })?;
468
469        debug_assert!(idx <= array.len());
470
471        if idx < array.len() {
472            // element exists in the array, we either need to replace it or continue
473            // depending on whether this is the last token or not
474            if remaining.is_root() {
475                // last token, we replace the value and call it a day
476                Ok(Assigned::Done(Some(mem::replace(&mut array[idx], src))))
477            } else {
478                // not the last token, we continue with a mut ref to the element as
479                // the next value
480                Ok(Assigned::Continue {
481                    next_dest: &mut array[idx],
482                    same_value: src,
483                })
484            }
485        } else {
486            // element does not exist in the array.
487            // we create the path and assign the value
488            let src = expand(remaining, src);
489            array.push(src);
490            Ok(Assigned::Done(None))
491        }
492    }
493
494    #[allow(clippy::needless_pass_by_value)]
495    fn assign_object<'v>(
496        token: Token<'_>,
497        remaining: &Pointer,
498        obj: &'v mut Map<String, Value>,
499        src: Value,
500    ) -> Assigned<'v, Value> {
501        // grabbing the entry of the token
502        match obj.entry(token.to_string()) {
503            Entry::Occupied(entry) => {
504                // if the entry exists, we either replace it or continue
505                let entry = entry.into_mut();
506                if remaining.is_root() {
507                    // if this is the last token, we are done
508                    // grab the old value and replace it with the new one
509                    Assigned::Done(Some(mem::replace(entry, src)))
510                } else {
511                    // if this is not the last token, we continue with a mutable
512                    // reference to the entry as the next value
513                    Assigned::Continue {
514                        same_value: src,
515                        next_dest: entry,
516                    }
517                }
518            }
519            Entry::Vacant(entry) => {
520                // if the entry does not exist, we create a value based on the
521                // remaining path with the src value as a leaf and assign it to the
522                // entry
523                entry.insert(expand(remaining, src));
524                Assigned::Done(None)
525            }
526        }
527    }
528
529    fn assign_scalar<'v>(
530        remaining: &Pointer,
531        scalar: &'v mut Value,
532        value: Value,
533    ) -> Assigned<'v, Value> {
534        // scalar values are always replaced at the current buf (with its token)
535        // build the new src and we replace the value with it.
536        Assigned::Done(Some(mem::replace(scalar, expand(remaining, value))))
537    }
538}
539
540/*
541░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
542╔══════════════════════════════════════════════════════════════════════════════╗
543║                                                                              ║
544║                                    Tests                                     ║
545║                                   ¯¯¯¯¯¯¯                                    ║
546╚══════════════════════════════════════════════════════════════════════════════╝
547░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
548*/
549
550#[cfg(test)]
551#[allow(clippy::too_many_lines)]
552mod tests {
553    use super::{Assign, AssignError};
554    use crate::{
555        index::{OutOfBoundsError, ParseIndexError},
556        Pointer,
557    };
558    use alloc::str::FromStr;
559    use core::fmt::{Debug, Display};
560
561    #[derive(Debug)]
562    struct Test<V: Assign> {
563        data: V,
564        ptr: &'static str,
565        assign: V,
566        expected_data: V,
567        expected: Result<Option<V>, V::Error>,
568    }
569
570    impl<V> Test<V>
571    where
572        V: Assign + Clone + PartialEq + Display + Debug,
573        V::Value: Debug + PartialEq + From<V>,
574        V::Error: Debug + PartialEq,
575        Result<Option<V>, V::Error>: PartialEq<Result<Option<V::Value>, V::Error>>,
576    {
577        fn all(tests: impl IntoIterator<Item = Test<V>>) {
578            tests.into_iter().enumerate().for_each(|(i, t)| t.run(i));
579        }
580        fn run(self, i: usize) {
581            let Test {
582                ptr,
583                mut data,
584                assign,
585                expected_data,
586                expected,
587                ..
588            } = self;
589            let ptr = Pointer::from_static(ptr);
590            let replaced = ptr.assign(&mut data, assign.clone());
591            assert_eq!(
592                &expected_data, &data,
593                "test #{i}:\n\ndata: \n{data:#?}\n\nexpected_data\n{expected_data:#?}"
594            );
595            assert_eq!(&expected, &replaced);
596        }
597    }
598
599    /*
600    ╔═══════════════════════════════════════════════════╗
601    ║                        json                       ║
602    ╚═══════════════════════════════════════════════════╝
603    */
604
605    #[test]
606    #[cfg(feature = "json")]
607    fn assign_json() {
608        use alloc::vec;
609        use serde_json::json;
610        Test::all([
611            Test {
612                ptr: "/foo",
613                data: json!({}),
614                assign: json!("bar"),
615                expected_data: json!({"foo": "bar"}),
616                expected: Ok(None),
617            },
618            Test {
619                ptr: "",
620                data: json!({"foo": "bar"}),
621                assign: json!("baz"),
622                expected_data: json!("baz"),
623                expected: Ok(Some(json!({"foo": "bar"}))),
624            },
625            Test {
626                ptr: "/foo",
627                data: json!({"foo": "bar"}),
628                assign: json!("baz"),
629                expected_data: json!({"foo": "baz"}),
630                expected: Ok(Some(json!("bar"))),
631            },
632            Test {
633                ptr: "/foo/bar",
634                data: json!({"foo": "bar"}),
635                assign: json!("baz"),
636                expected_data: json!({"foo": {"bar": "baz"}}),
637                expected: Ok(Some(json!("bar"))),
638            },
639            Test {
640                ptr: "/foo/bar",
641                data: json!({}),
642                assign: json!("baz"),
643                expected_data: json!({"foo": {"bar": "baz"}}),
644                expected: Ok(None),
645            },
646            Test {
647                ptr: "/",
648                data: json!({}),
649                assign: json!("foo"),
650                expected_data: json!({"": "foo"}),
651                expected: Ok(None),
652            },
653            Test {
654                ptr: "/-",
655                data: json!({}),
656                assign: json!("foo"),
657                expected_data: json!({"-": "foo"}),
658                expected: Ok(None),
659            },
660            Test {
661                ptr: "/-",
662                data: json!(null),
663                assign: json!(34),
664                expected_data: json!([34]),
665                expected: Ok(Some(json!(null))),
666            },
667            Test {
668                ptr: "/foo/-",
669                data: json!({"foo": "bar"}),
670                assign: json!("baz"),
671                expected_data: json!({"foo": ["baz"]}),
672                expected: Ok(Some(json!("bar"))),
673            },
674            Test {
675                ptr: "/foo/-/bar",
676                assign: "baz".into(),
677                data: json!({}),
678                expected: Ok(None),
679                expected_data: json!({"foo":[{"bar": "baz"}]}),
680            },
681            Test {
682                ptr: "/foo/-/bar",
683                assign: "qux".into(),
684                data: json!({"foo":[{"bar":"baz" }]}),
685                expected: Ok(None),
686                expected_data: json!({"foo":[{"bar":"baz"},{"bar":"qux"}]}),
687            },
688            Test {
689                ptr: "/foo/-/bar",
690                data: json!({"foo":[{"bar":"baz"},{"bar":"qux"}]}),
691                assign: "quux".into(),
692                expected: Ok(None),
693                expected_data: json!({"foo":[{"bar":"baz"},{"bar":"qux"},{"bar":"quux"}]}),
694            },
695            Test {
696                ptr: "/foo/0/bar",
697                data: json!({"foo":[{"bar":"baz"},{"bar":"qux"},{"bar":"quux"}]}),
698                assign: "grault".into(),
699                expected: Ok(Some("baz".into())),
700                expected_data: json!({"foo":[{"bar":"grault"},{"bar":"qux"},{"bar":"quux"}]}),
701            },
702            Test {
703                ptr: "/0",
704                data: json!({}),
705                assign: json!("foo"),
706                expected_data: json!({"0": "foo"}),
707                expected: Ok(None),
708            },
709            Test {
710                ptr: "/1",
711                data: json!(null),
712                assign: json!("foo"),
713                expected_data: json!({"1": "foo"}),
714                expected: Ok(Some(json!(null))),
715            },
716            Test {
717                ptr: "/0",
718                data: json!([]),
719                expected_data: json!(["foo"]),
720                assign: json!("foo"),
721                expected: Ok(None),
722            },
723            Test {
724                ptr: "///bar",
725                data: json!({"":{"":{"bar": 42}}}),
726                assign: json!(34),
727                expected_data: json!({"":{"":{"bar":34}}}),
728                expected: Ok(Some(json!(42))),
729            },
730            Test {
731                ptr: "/1",
732                data: json!([]),
733                assign: json!("foo"),
734                expected: Err(AssignError::OutOfBounds {
735                    offset: 0,
736                    source: OutOfBoundsError {
737                        index: 1,
738                        length: 0,
739                    },
740                }),
741                expected_data: json!([]),
742            },
743            Test {
744                ptr: "/0",
745                data: json!(["foo"]),
746                assign: json!("bar"),
747                expected: Ok(Some(json!("foo"))),
748                expected_data: json!(["bar"]),
749            },
750            Test {
751                ptr: "/a",
752                data: json!([]),
753                assign: json!("foo"),
754                expected: Err(AssignError::FailedToParseIndex {
755                    offset: 0,
756                    source: ParseIndexError::InvalidInteger(usize::from_str("foo").unwrap_err()),
757                }),
758                expected_data: json!([]),
759            },
760            Test {
761                ptr: "/002",
762                data: json!([]),
763                assign: json!("foo"),
764                expected: Err(AssignError::FailedToParseIndex {
765                    offset: 0,
766                    source: ParseIndexError::LeadingZeros,
767                }),
768                expected_data: json!([]),
769            },
770        ]);
771    }
772
773    /*
774    ╔══════════════════════════════════════════════════╗
775    ║                       toml                       ║
776    ╚══════════════════════════════════════════════════╝
777    */
778
779    #[test]
780    #[cfg(feature = "toml")]
781    fn assign_toml() {
782        use alloc::vec;
783        use toml::{toml, Table, Value};
784        Test::all([
785            Test {
786                data: Value::Table(toml::Table::new()),
787                ptr: "/foo",
788                assign: "bar".into(),
789                expected_data: toml! { "foo" = "bar" }.into(),
790                expected: Ok(None),
791            },
792            Test {
793                data: toml! {foo =  "bar"}.into(),
794                ptr: "",
795                assign: "baz".into(),
796                expected_data: "baz".into(),
797                expected: Ok(Some(toml! {foo =  "bar"}.into())),
798            },
799            Test {
800                data: toml! { foo = "bar"}.into(),
801                ptr: "/foo",
802                assign: "baz".into(),
803                expected_data: toml! {foo = "baz"}.into(),
804                expected: Ok(Some("bar".into())),
805            },
806            Test {
807                data: toml! { foo = "bar"}.into(),
808                ptr: "/foo/bar",
809                assign: "baz".into(),
810                expected_data: toml! {foo = { bar = "baz"}}.into(),
811                expected: Ok(Some("bar".into())),
812            },
813            Test {
814                data: Table::new().into(),
815                ptr: "/",
816                assign: "foo".into(),
817                expected_data: toml! {"" =  "foo"}.into(),
818                expected: Ok(None),
819            },
820            Test {
821                data: Table::new().into(),
822                ptr: "/-",
823                assign: "foo".into(),
824                expected_data: toml! {"-" = "foo"}.into(),
825                expected: Ok(None),
826            },
827            Test {
828                data: "data".into(),
829                ptr: "/-",
830                assign: 34.into(),
831                expected_data: Value::Array(vec![34.into()]),
832                expected: Ok(Some("data".into())),
833            },
834            Test {
835                data: toml! {foo = "bar"}.into(),
836                ptr: "/foo/-",
837                assign: "baz".into(),
838                expected_data: toml! {foo =  ["baz"]}.into(),
839                expected: Ok(Some("bar".into())),
840            },
841            Test {
842                data: Table::new().into(),
843                ptr: "/0",
844                assign: "foo".into(),
845                expected_data: toml! {"0" = "foo"}.into(),
846                expected: Ok(None),
847            },
848            Test {
849                data: 21.into(),
850                ptr: "/1",
851                assign: "foo".into(),
852                expected_data: toml! {"1" = "foo"}.into(),
853                expected: Ok(Some(21.into())),
854            },
855            Test {
856                data: Value::Array(vec![]),
857                ptr: "/0",
858                expected_data: vec![Value::from("foo")].into(),
859                assign: "foo".into(),
860                expected: Ok(None),
861            },
862            Test {
863                ptr: "/foo/-/bar",
864                assign: "baz".into(),
865                data: Table::new().into(),
866                expected: Ok(None),
867                expected_data: toml! { "foo" = [{"bar" = "baz"}] }.into(),
868            },
869            Test {
870                ptr: "/foo/-/bar",
871                assign: "qux".into(),
872                data: toml! {"foo" = [{"bar" = "baz"}] }.into(),
873                expected: Ok(None),
874                expected_data: toml! {"foo" = [{"bar" = "baz"}, {"bar" = "qux"}]}.into(),
875            },
876            Test {
877                ptr: "/foo/-/bar",
878                data: toml! {"foo" = [{"bar" = "baz"}, {"bar" = "qux"}]}.into(),
879                assign: "quux".into(),
880                expected: Ok(None),
881                expected_data: toml! {"foo" = [{"bar" = "baz"}, {"bar" = "qux"}, {"bar" = "quux"}]}
882                    .into(),
883            },
884            Test {
885                ptr: "/foo/0/bar",
886                data: toml! {"foo" = [{"bar" = "baz"}, {"bar" = "qux"}, {"bar" = "quux"}]}.into(),
887                assign: "grault".into(),
888                expected: Ok(Some("baz".into())),
889                expected_data:
890                    toml! {"foo" = [{"bar" = "grault"}, {"bar" = "qux"}, {"bar" = "quux"}]}.into(),
891            },
892            Test {
893                data: Value::Array(vec![]),
894                ptr: "/-",
895                assign: "foo".into(),
896                expected: Ok(None),
897                expected_data: vec!["foo"].into(),
898            },
899            Test {
900                data: Value::Array(vec![]),
901                ptr: "/1",
902                assign: "foo".into(),
903                expected: Err(AssignError::OutOfBounds {
904                    offset: 0,
905                    source: OutOfBoundsError {
906                        index: 1,
907                        length: 0,
908                    },
909                }),
910                expected_data: Value::Array(vec![]),
911            },
912            Test {
913                data: Value::Array(vec![]),
914                ptr: "/a",
915                assign: "foo".into(),
916                expected: Err(AssignError::FailedToParseIndex {
917                    offset: 0,
918                    source: ParseIndexError::InvalidInteger(usize::from_str("foo").unwrap_err()),
919                }),
920                expected_data: Value::Array(vec![]),
921            },
922        ]);
923    }
924}