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