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