texcraft_stdext/collections/
groupingmap.rs

1//! Containers with a grouping concept such that mutations are rolled back at the end of each group.
2//!
3//! This module provides a wrapper type [GroupingContainer] that wraps associative containers
4//! to give them a particular kind of grouping semantics.
5//! It can wrap any type satisfying the [BackingContainer] trait.
6//! In the wrapped container, a group is started and finished using the
7//! [begin_group](GroupingContainer::begin_group) and
8//! [end_group](GroupingContainer::end_group) methods.
9//! The grouping semantics are: all mutations performed on the container
10//!     during the group are rolled back at the end of the group.
11//!
12//! The module also provides implementations where the backing container is a
13//! [HashMap] ([GroupingHashMap]) and a vector ([GroupingVec]).
14//!
15//! # Examples
16//!
17//! These examples all use the [GroupingHashMap] type.
18//! The same semantics will apply to any wrapped container.
19//!
20//! The basic associative methods are the same as the standard hash map.
21//! ```
22//! # use texcraft_stdext::collections::groupingmap::GroupingHashMap;
23//! # use texcraft_stdext::collections::groupingmap::Scope;
24//! let mut cat_colors = GroupingHashMap::default();
25//! cat_colors.insert("mint", "ginger", Scope::Local);
26//! assert_eq!(cat_colors.get(&"mint"), Some(&"ginger"));
27//! ```
28//! The grouping methods are the main addition.
29//! ```
30//! # use texcraft_stdext::collections::groupingmap::GroupingHashMap;
31//! # use texcraft_stdext::collections::groupingmap::Scope;
32//! let mut cat_colors = GroupingHashMap::default();
33//!
34//! // Insert a new value, update the value in a new group, and then end the group to roll back
35//! // the update.
36//! cat_colors.insert("paganini", "black", Scope::Local);
37//! cat_colors.begin_group();
38//! cat_colors.insert("paganini", "gray", Scope::Local);
39//! assert_eq!(cat_colors.get(&"paganini"), Some(&"gray"));
40//! assert_eq!(cat_colors.end_group(), Ok(()));
41//! assert_eq!(cat_colors.get(&"paganini"), Some(&"black"));
42//!
43//! // Begin a new group, insert a value, and then end the group to roll back the insert.
44//! cat_colors.begin_group();
45//! cat_colors.insert("mint", "ginger", Scope::Local);
46//! assert_eq!(cat_colors.get(&"mint"), Some(&"ginger"));
47//! assert_eq!(cat_colors.end_group(), Ok(()));
48//! assert_eq!(cat_colors.get(&"mint"), None);
49//! ```
50//! The `end_group` method returns an error if there is no group to end.
51//! ```
52//! # use texcraft_stdext::collections::groupingmap::GroupingHashMap;
53//! # use texcraft_stdext::collections::groupingmap::Scope;
54//! # use texcraft_stdext::collections::groupingmap::NoGroupToEndError;
55//! let mut cat_colors = GroupingHashMap::<String, String>::default();
56//! assert_eq!(cat_colors.end_group(), Err(NoGroupToEndError{}));
57//! ```
58//! There is also a "global" variant of the `insert` method. It inserts the value at the global
59//! group, and erases all other values.
60//! ```
61//! # use texcraft_stdext::collections::groupingmap::GroupingHashMap;
62//! # use texcraft_stdext::collections::groupingmap::Scope;
63//! let mut cat_colors = GroupingHashMap::default();
64//! cat_colors.insert("paganini", "black", Scope::Local);
65//! cat_colors.begin_group();
66//! cat_colors.insert("paganini", "gray", Scope::Global);
67//! assert_eq!(cat_colors.end_group(), Ok(()));
68//! assert_eq!(cat_colors.get(&"paganini"), Some(&"gray"));
69//! ```
70//!
71use std::collections::hash_map::Entry;
72use std::collections::HashMap;
73use std::hash::Hash;
74
75/// Trait for containers that can be wrapped using [GroupingContainer].
76pub trait BackingContainer<K, V>: Default {
77    /// Set the value at the provided key.
78    fn insert(&mut self, k: K, v: V);
79
80    /// Get a reference to the value at the provided key, or `None` if the value doesn't exist.
81    fn get(&self, k: &K) -> Option<&V>;
82
83    /// Get mutable a reference to the value at the provided key, or `None` if the value doesn't exist.
84    fn get_mut(&mut self, k: &K) -> Option<&mut V>;
85
86    /// Remove a value with the provided key, if it exists.
87    fn remove(&mut self, k: &K);
88
89    /// Type of iterator returned by the [BackingContainer::iter] method.
90    type Iter<'a>: Iterator<Item = (K, &'a V)>
91    where
92        V: 'a,
93        Self: 'a;
94
95    /// Iterate over all (key, value) tuples in the container.
96    fn iter(&self) -> Self::Iter<'_>;
97
98    /// Return the number of elements in the container.
99    fn len(&self) -> usize;
100
101    /// Return whether the container is empty.
102    fn is_empty(&self) -> bool {
103        self.len() == 0
104    }
105}
106
107impl<K: Eq + Hash + Clone, V> BackingContainer<K, V> for HashMap<K, V> {
108    #[inline]
109    fn insert(&mut self, k: K, v: V) {
110        HashMap::insert(self, k, v);
111    }
112    #[inline]
113    fn get(&self, k: &K) -> Option<&V> {
114        HashMap::get(self, k)
115    }
116    #[inline]
117    fn get_mut(&mut self, k: &K) -> Option<&mut V> {
118        HashMap::get_mut(self, k)
119    }
120    #[inline]
121    fn remove(&mut self, k: &K) {
122        HashMap::remove(self, k);
123    }
124    type Iter<'a>
125        = std::iter::Map<
126        std::collections::hash_map::Iter<'a, K, V>,
127        fn(i: (&'a K, &'a V)) -> (K, &'a V),
128    >
129    where
130        K: 'a,
131        V: 'a;
132    fn iter(&self) -> Self::Iter<'_> {
133        HashMap::iter(self).map(map_func)
134    }
135    fn len(&self) -> usize {
136        HashMap::len(self)
137    }
138}
139
140fn map_func<'a, K: Clone, V>(i: (&'a K, &'a V)) -> (K, &'a V) {
141    (i.0.clone(), i.1)
142}
143
144impl<V> BackingContainer<usize, V> for Vec<Option<V>> {
145    #[inline]
146    fn insert(&mut self, k: usize, v: V) {
147        match <[Option<V>]>::get_mut(self, k) {
148            None => {
149                self.resize_with(k, Default::default);
150                self.push(Some(v))
151            }
152            Some(elem) => {
153                *elem = Some(v);
154            }
155        }
156    }
157
158    #[inline]
159    fn get(&self, k: &usize) -> Option<&V> {
160        match <[Option<V>]>::get(self, *k) {
161            None => None,
162            Some(v) => v.as_ref(),
163        }
164    }
165
166    #[inline]
167    fn get_mut(&mut self, k: &usize) -> Option<&mut V> {
168        match <[Option<V>]>::get_mut(self, *k) {
169            None => None,
170            Some(v) => v.as_mut(),
171        }
172    }
173
174    #[inline]
175    fn remove(&mut self, k: &usize) {
176        if let Some(elem) = <[Option<V>]>::get_mut(self, *k) {
177            *elem = None;
178        }
179    }
180
181    type Iter<'a>
182        = std::iter::FilterMap<
183        std::iter::Enumerate<std::slice::Iter<'a, Option<V>>>,
184        fn(i: (usize, &'a Option<V>)) -> Option<(usize, &'a V)>,
185    >
186    where
187        V: 'a;
188    fn iter(&self) -> Self::Iter<'_> {
189        <[Option<V>]>::iter(self)
190            .enumerate()
191            .filter_map(|i| i.1.as_ref().map(|v| (i.0, v)))
192    }
193
194    fn len(&self) -> usize {
195        let mut l = 0;
196        for v in <[Option<V>]>::iter(self) {
197            if v.is_some() {
198                l += 1;
199            }
200        }
201        l
202    }
203}
204
205/// A wrapper around [BackingContainer] types that adds a specific kind of group semantics.
206///
207/// See the module docs for more information.
208#[derive(Debug)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210pub struct GroupingContainer<K, V, T> {
211    backing_container: T,
212
213    // The groups stack does not contain the global group as no cleanup there is needed.
214    #[cfg_attr(
215        feature = "serde",
216        serde(bound(
217            deserialize = "K: Eq + Hash + serde::Deserialize<'de>, V: serde::Deserialize<'de>"
218        ))
219    )]
220    groups: Vec<HashMap<K, EndOfGroupAction<V>>>,
221}
222
223/// A grouping container based on the [HashMap] type.
224pub type GroupingHashMap<K, V> = GroupingContainer<K, V, HashMap<K, V>>;
225
226/// A grouping container based on the [Vec] type.
227///
228/// The vector is given map semantics with keys of type [usize], which are used as
229/// indices for the vector.
230/// When inserting an element at a key, the vector is extended if needed so that it can
231/// hold an element with that index.
232pub type GroupingVec<V> = GroupingContainer<usize, V, Vec<Option<V>>>;
233
234/// Scope is used in the insertion method to determine the scope to insert at.
235#[derive(Clone, Copy, Debug)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237pub enum Scope {
238    /// Insertions in the local scope are rolled back at the end of the current group.
239    Local,
240    /// Insertions in the global scope erase any other insertions for the same key, and
241    /// persist beyond the end of the current groups.
242    Global,
243}
244
245#[derive(Debug, PartialEq, Eq)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
247enum EndOfGroupAction<V> {
248    Revert(V),
249    Delete,
250}
251
252/// Error returned if there is no group to end when [GroupingContainer::end_group] is invoked.
253#[derive(Debug, PartialEq, Eq)]
254pub struct NoGroupToEndError;
255
256impl<K: Eq + Hash + Clone, V, T: BackingContainer<K, V>> GroupingContainer<K, V, T> {
257    /// Inserts the key, value pair in the provided scope.
258    pub fn insert(&mut self, key: K, mut val: V, scope: Scope) -> bool {
259        let group = match scope {
260            Scope::Local => self.groups.last_mut(),
261            Scope::Global => {
262                for group in &mut self.groups {
263                    group.remove(&key);
264                }
265                None
266            }
267        };
268        match (self.backing_container.get_mut(&key), group) {
269            (None, None) => {
270                self.backing_container.insert(key, val);
271                false
272            }
273            (None, Some(group)) => {
274                group.insert(key.clone(), EndOfGroupAction::Delete);
275                self.backing_container.insert(key, val);
276                false
277            }
278            (Some(val_ref), None) => {
279                *val_ref = val;
280                true
281            }
282            (Some(val_ref), Some(group)) => {
283                std::mem::swap(&mut val, val_ref);
284                if let Entry::Vacant(vac) = group.entry(key) {
285                    vac.insert(EndOfGroupAction::Revert(val));
286                };
287                true
288            }
289        }
290    }
291
292    /// Retrieves the value at the provided key.
293    ///
294    /// It is equivalent to obtaining the
295    /// backing container using [backing_container](GroupingContainer::backing_container)
296    /// and calling the [get](BackingContainer::get) method.
297    #[inline]
298    pub fn get(&self, key: &K) -> Option<&V> {
299        self.backing_container.get(key)
300    }
301
302    /// Begins a new group.
303    pub fn begin_group(&mut self) {
304        // Note that `HashSet::new()` is basically a free operation: no allocations will occur
305        // until elements are inserted into it. So even if no mutations are made in this group, we
306        // don't pay much for adding the set eagerly.
307        self.groups.push(HashMap::new());
308    }
309
310    /// Attempts to end the current group. Returns an error if there is no group to end.
311    pub fn end_group(&mut self) -> Result<(), NoGroupToEndError> {
312        match self.groups.pop() {
313            None => Err(NoGroupToEndError {}),
314            Some(group) => {
315                // Note that for the running time analysis we account each iteration of this loop
316                // to the insert method that put the key in the changed_keys set. Put another way,
317                // this can be considered a defer or cleanup step for all of the insert calls
318                // in the group that is being ended.
319                for (key, value) in group.into_iter() {
320                    match value {
321                        EndOfGroupAction::Delete => {
322                            self.backing_container.remove(&key);
323                        }
324                        EndOfGroupAction::Revert(old_val) => {
325                            self.backing_container.insert(key, old_val);
326                        }
327                    }
328                }
329                Ok(())
330            }
331        }
332    }
333
334    /// Extends the `GroupingMap` with (key, value) pairs.
335    /// ```
336    /// # use texcraft_stdext::collections::groupingmap::*;
337    /// let mut cat_colors = GroupingHashMap::default();
338    /// cat_colors.extend(std::array::IntoIter::new([
339    ///    ("paganini", "black"),
340    ///    ("mint", "ginger"),
341    /// ]));
342    /// assert_eq!(cat_colors.get(&"paganini"), Some(&"black"));
343    /// assert_eq!(cat_colors.get(&"mint"), Some(&"ginger"));
344    /// ```
345    pub fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
346        for (key, val) in iter {
347            self.insert(key, val, Scope::Local);
348        }
349    }
350
351    /// Gets an immutable reference to the backing container.
352    ///
353    /// It is not possible to obtain a mutable reference to the backing container, as
354    /// mutations applied through such a reference could not be rolled back.
355    #[inline]
356    pub fn backing_container(&self) -> &T {
357        &self.backing_container
358    }
359
360    /// Iterate over all (key, value) tuples that are currently visible.
361    pub fn iter(&self) -> T::Iter<'_> {
362        self.backing_container.iter()
363    }
364
365    /// Iterate over all (key, value) tuples in the container,
366    ///     including tuples that are not currently visible.
367    ///
368    /// See the documentation on [IterAll] for information on how this iterator works.
369    ///
370    /// To iterate over visible items only, use the [GroupingContainer::iter] method.
371    pub fn iter_all(&self) -> IterAll<'_, K, V, T> {
372        IterAll::new(self)
373    }
374
375    /// Returns the number of elements in the container.
376    pub fn len(&self) -> usize {
377        self.backing_container.len()
378    }
379
380    /// Returns whether the container is empty.
381    pub fn is_empty(&self) -> bool {
382        self.backing_container.is_empty()
383    }
384}
385
386impl<K, V, T: Default> Default for GroupingContainer<K, V, T> {
387    fn default() -> Self {
388        Self {
389            backing_container: Default::default(),
390            groups: Default::default(),
391        }
392    }
393}
394
395impl<K: Eq + Hash, V: PartialEq, T: PartialEq> PartialEq for GroupingContainer<K, V, T> {
396    fn eq(&self, other: &Self) -> bool {
397        self.backing_container == other.backing_container && self.groups == other.groups
398    }
399}
400
401impl<K: Eq + Hash, V: Eq, T: Eq> Eq for GroupingContainer<K, V, T> {}
402
403/// The item for the [IterAll] iterator.
404#[derive(PartialEq, Eq, Debug)]
405pub enum Item<T> {
406    /// Begin a new group.
407    BeginGroup,
408    /// Insert the `T=(key, value)` tuple into the container.
409    Value(T),
410}
411
412impl<T> Item<T> {
413    /// Adapt a lambda to use in [Iterator::map] for iterators over this item.
414    ///
415    /// When iterating over items of this type, one almost always wants to keep
416    ///     [Item::BeginGroup] constant and apply a transformation to the [Item::Value] variant.
417    /// This adaptor function helps with this by converting a lambda that operates on `T`
418    ///     to a lambda that operates on [`Item<T>`].
419    ///
420    /// ```
421    /// # use texcraft_stdext::collections::groupingmap::*;
422    /// let start: Vec<Item<usize>> = vec![
423    ///     Item::Value(1),
424    ///     Item::BeginGroup,
425    ///     Item::Value(2),
426    /// ];
427    /// let end: Vec<Item<usize>> = start.into_iter().map(Item::adapt_map(|i| { i *100 })).collect();
428    /// assert_eq![end, vec![
429    ///     Item::Value(100),
430    ///     Item::BeginGroup,
431    ///     Item::Value(200),
432    /// ]];
433    /// ```
434    pub fn adapt_map<B, F: FnMut(T) -> B>(mut f: F) -> impl FnMut(Item<T>) -> Item<B> {
435        move |item| match item {
436            Item::BeginGroup => Item::BeginGroup,
437            Item::Value(kv) => Item::Value(f(kv)),
438        }
439    }
440}
441
442/// An iterator over all values in a grouping container, including invisible values.
443///
444/// To understand this iterator, it's easiest to look at an example.
445/// Suppose we have the following grouping map:
446/// ```
447/// # use texcraft_stdext::collections::groupingmap::*;
448/// let mut cat_colors = GroupingHashMap::default();
449/// cat_colors.insert("paganini", "black", Scope::Local);
450/// cat_colors.begin_group();
451/// cat_colors.insert("paganini", "gray", Scope::Local);
452/// ```
453/// After these mutations, the grouping map contains two tuples:
454///     the tuple `("paganini", "gray")` that is visible, and
455///     the tuple `("paganini", "black")` that is currently invisible.
456/// The second tuple will become visible again when the group ends.
457///
458/// This iterator enables iterating over all tuples, visible and invisible.
459/// In this example here this is the result:
460/// ```
461/// # use texcraft_stdext::collections::groupingmap::*;
462/// # let mut cat_colors = GroupingHashMap::default();
463/// # cat_colors.insert("paganini", "black", Scope::Local);
464/// # cat_colors.begin_group();
465/// # cat_colors.insert("paganini", "gray", Scope::Local);
466/// let items: Vec<_> = cat_colors.iter_all().collect();
467/// assert_eq![items, vec![
468///     Item::Value(("paganini", &"black")),
469///     Item::BeginGroup,
470///     Item::Value(("paganini", &"gray")),
471/// ]]
472/// ```
473/// A good mental model for this iterator is that it replays all mutations (inserts and begin groups)
474///     that have been applied to the map.
475/// However it doesn't replay the mutations exactly as they happened.
476/// Instead, it returns the minimal number of mutations to recreate the map.
477/// Thus:
478/// ```
479/// # use texcraft_stdext::collections::groupingmap::*;
480/// let mut cat_colors = GroupingHashMap::default();
481/// cat_colors.insert("local", "value_1", Scope::Local);
482/// cat_colors.insert("local", "value_2", Scope::Local);
483/// cat_colors.begin_group();
484/// cat_colors.insert("local", "value_3", Scope::Local);
485/// cat_colors.insert("local", "value_4", Scope::Local);
486/// let items: Vec<_> = cat_colors.iter_all().collect();
487/// assert_eq![items, vec![
488///     Item::Value(("local", &"value_2")),
489///     Item::BeginGroup,
490///     Item::Value(("local", &"value_4")),
491/// ]]
492/// ```
493/// It also converts global assignments within a group to regular assignments in the global scope:
494/// ```
495/// # use texcraft_stdext::collections::groupingmap::*;
496/// let mut cat_colors = GroupingHashMap::default();
497/// cat_colors.insert("global", "value_1", Scope::Local);
498/// cat_colors.begin_group();
499/// cat_colors.insert("global", "value_2", Scope::Global);
500/// let items: Vec<_> = cat_colors.iter_all().collect();
501/// assert_eq![items, vec![
502///     Item::Value(("global", &"value_2")),
503///     Item::BeginGroup,
504/// ]]
505/// ```
506pub struct IterAll<'a, K, V, T: BackingContainer<K, V> + 'a> {
507    visible_items: Option<T::Iter<'a>>,
508    non_global_items: Vec<Item<(K, &'a V)>>,
509    key_to_val: HashMap<K, Option<&'a V>>,
510}
511
512impl<'a, K: Eq + Hash + Clone, V, T: BackingContainer<K, V>> IterAll<'a, K, V, T> {
513    fn new(map: &'a GroupingContainer<K, V, T>) -> Self {
514        let mut key_to_val = HashMap::<K, Option<&'a V>>::with_capacity(map.groups.len());
515        let save_stack_size: usize = map.groups.iter().map(HashMap::len).sum();
516        let mut non_global_items = Vec::<Item<(K, &'a V)>>::with_capacity(save_stack_size);
517        for group in map.groups.iter().rev() {
518            for (k, action) in group {
519                let v = match key_to_val.get(k) {
520                    None => map.backing_container.get(k).unwrap(),
521                    Some(v) => v.unwrap(),
522                };
523                non_global_items.push(Item::Value((k.clone(), v)));
524                key_to_val.insert(
525                    k.clone(),
526                    match action {
527                        EndOfGroupAction::Delete => None,
528                        EndOfGroupAction::Revert(v) => Some(v),
529                    },
530                );
531            }
532            non_global_items.push(Item::BeginGroup);
533        }
534        Self {
535            visible_items: Some(map.backing_container().iter()),
536            non_global_items,
537            key_to_val,
538        }
539    }
540}
541
542impl<'a, K: Eq + Hash, V, T: BackingContainer<K, V> + 'a> Iterator for IterAll<'a, K, V, T> {
543    type Item = Item<(K, &'a V)>;
544
545    fn next(&mut self) -> Option<Self::Item> {
546        if let Some(visible_items) = &mut self.visible_items {
547            for visible_item in visible_items {
548                match self.key_to_val.get(&visible_item.0) {
549                    // The item is visible and appears nowhere in the save stack. It must have been defined
550                    // in the global scope, and we thus return it.
551                    None => return Some(Item::Value((visible_item.0, visible_item.1))),
552                    // The item is visible and the last entry in the save stack is a delete instruction.
553                    // This indicates the item was first defined inside a local group and is not defined in
554                    // the global scope. We skip it.
555                    Some(None) => continue,
556                    // The item is visible and the last entry in the save stack is a revert instruction.
557                    // We return the value in the revert instruction, as this is the value in the global scope.
558                    Some(Some(global_value)) => {
559                        return Some(Item::Value((visible_item.0, global_value)))
560                    }
561                }
562            }
563        }
564        self.visible_items = None;
565        self.non_global_items.pop()
566    }
567}
568
569impl<K: Eq + Hash + Clone, V, T: BackingContainer<K, V>> FromIterator<Item<(K, V)>>
570    for GroupingContainer<K, V, T>
571{
572    fn from_iter<I: IntoIterator<Item = Item<(K, V)>>>(iter: I) -> Self {
573        let mut map: Self = GroupingContainer::default();
574        for item in iter {
575            match item {
576                Item::BeginGroup => map.begin_group(),
577                Item::Value((k, v)) => {
578                    map.insert(k, v, Scope::Local);
579                }
580            }
581        }
582        map
583    }
584}
585
586impl<K: Eq + Hash + Clone, V, T: BackingContainer<K, V>> FromIterator<(K, V)>
587    for GroupingContainer<K, V, T>
588{
589    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
590        let mut map: Self = GroupingContainer::default();
591        for (k, v) in iter {
592            map.backing_container.insert(k, v);
593        }
594        map
595    }
596}
597
598#[cfg(test)]
599mod tests {
600    use crate::collections::groupingmap::*;
601
602    #[test]
603    fn insert_after_nested_insert() {
604        let mut map = GroupingHashMap::default();
605        map.begin_group();
606        map.insert(3, 5, Scope::Local);
607        assert_eq!(map.end_group(), Ok(()));
608        assert_eq!(map.get(&3), None);
609        map.insert(3, 4, Scope::Local);
610        assert_eq!(map.get(&3), Some(&4));
611    }
612
613    #[test]
614    fn insert_global_after_no_insert() {
615        let mut map = GroupingHashMap::default();
616        map.begin_group();
617        map.insert(3, 5, Scope::Global);
618        assert_eq!(map.end_group(), Ok(()));
619        assert_eq!(map.get(&3), Some(&5));
620    }
621
622    fn run_iter_all_test(map: &GroupingHashMap<usize, usize>, want: &[Item<(usize, usize)>]) {
623        let got: Vec<_> = map
624            .iter_all()
625            .map(|item| match item {
626                Item::BeginGroup => Item::BeginGroup,
627                Item::Value((k, v)) => Item::Value((k, *v)),
628            })
629            .collect();
630        assert_eq!(got, want);
631    }
632
633    macro_rules! iter_all_tests {
634        ( $( ($name: ident, $map: expr, $want: expr $(,)? ), )+ ) => {
635            $(
636            #[test]
637            fn $name() {
638                let map = $map;
639                let want = $want;
640                run_iter_all_test(&map, &want);
641            }
642            )+
643        };
644    }
645
646    mod iter_all_tests {
647        use super::*;
648        iter_all_tests!(
649            (empty_0, GroupingHashMap::default(), vec![]),
650            (
651                empty_1,
652                {
653                    let mut m = GroupingHashMap::default();
654                    m.begin_group();
655                    m
656                },
657                vec![Item::BeginGroup],
658            ),
659            (
660                empty_2,
661                {
662                    let mut m = GroupingHashMap::default();
663                    m.begin_group();
664                    m.begin_group();
665                    m.begin_group();
666                    m.end_group().unwrap();
667                    m
668                },
669                vec![Item::BeginGroup, Item::BeginGroup],
670            ),
671            (
672                single_root_assignment,
673                {
674                    let mut m = GroupingHashMap::default();
675                    m.insert(1, 1, Scope::Local);
676                    m.begin_group();
677                    m.begin_group();
678                    m
679                },
680                vec![Item::Value((1, 1)), Item::BeginGroup, Item::BeginGroup],
681            ),
682            (
683                single_global_assignment,
684                {
685                    let mut m = GroupingHashMap::default();
686                    m.begin_group();
687                    m.insert(1, 1, Scope::Global);
688                    m.begin_group();
689                    m
690                },
691                vec![Item::Value((1, 1)), Item::BeginGroup, Item::BeginGroup],
692            ),
693            (
694                overwrite_root_assignment_1,
695                {
696                    let mut m = GroupingHashMap::default();
697                    m.insert(1, 1, Scope::Local);
698                    m.begin_group();
699                    m.insert(1, 2, Scope::Local);
700                    m.begin_group();
701                    m
702                },
703                vec![
704                    Item::Value((1, 1)),
705                    Item::BeginGroup,
706                    Item::Value((1, 2)),
707                    Item::BeginGroup
708                ],
709            ),
710            (
711                overwrite_root_assignment_2,
712                {
713                    let mut m = GroupingHashMap::default();
714                    m.insert(1, 1, Scope::Local);
715                    m.begin_group();
716                    m.insert(1, 2, Scope::Local);
717                    m.begin_group();
718                    m.insert(1, 3, Scope::Local);
719                    m
720                },
721                vec![
722                    Item::Value((1, 1)),
723                    Item::BeginGroup,
724                    Item::Value((1, 2)),
725                    Item::BeginGroup,
726                    Item::Value((1, 3)),
727                ],
728            ),
729            (
730                single_local_assignment,
731                {
732                    let mut m = GroupingHashMap::default();
733                    m.begin_group();
734                    m.insert(1, 1, Scope::Local);
735                    m.begin_group();
736                    m
737                },
738                vec![Item::BeginGroup, Item::Value((1, 1)), Item::BeginGroup],
739            ),
740            (
741                overwrite_local_assignment_1,
742                {
743                    let mut m = GroupingHashMap::default();
744                    m.begin_group();
745                    m.insert(1, 1, Scope::Local);
746                    m.begin_group();
747                    m.insert(1, 2, Scope::Local);
748                    m
749                },
750                vec![
751                    Item::BeginGroup,
752                    Item::Value((1, 1)),
753                    Item::BeginGroup,
754                    Item::Value((1, 2))
755                ],
756            ),
757            (
758                overwrite_local_assignment_2,
759                {
760                    let mut m = GroupingHashMap::default();
761                    m.begin_group();
762                    m.insert(1, 1, Scope::Local);
763                    m.begin_group();
764                    m.insert(1, 2, Scope::Local);
765                    m.begin_group();
766                    m.insert(1, 3, Scope::Local);
767                    m
768                },
769                vec![
770                    Item::BeginGroup,
771                    Item::Value((1, 1)),
772                    Item::BeginGroup,
773                    Item::Value((1, 2)),
774                    Item::BeginGroup,
775                    Item::Value((1, 3))
776                ],
777            ),
778        );
779    }
780}