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}