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}