mz_ore/collections.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Collection utilities.
17
18use std::collections::BTreeMap;
19use std::collections::btree_map::Entry as BEntry;
20use std::collections::hash_map::Entry as HEntry;
21use std::fmt::{Debug, Display};
22
23mod hash;
24
25pub use crate::collections::hash::{HashMap, HashSet};
26
27/// Extension methods for collections.
28pub trait CollectionExt<T>: Sized
29where
30 T: IntoIterator,
31{
32 /// Consumes the collection and returns its first element.
33 ///
34 /// This method panics if the collection does not have at least one element.
35 fn into_first(self) -> T::Item;
36
37 /// Consumes the collection and returns its last element.
38 ///
39 /// This method panics if the collection does not have at least one element.
40 fn into_last(self) -> T::Item;
41
42 /// Consumes the collection and returns its only element.
43 ///
44 /// This method panics if the collection does not have exactly one element.
45 fn into_element(self) -> T::Item {
46 self.expect_element(|| "into_element called on collection without exactly one element")
47 }
48
49 /// Consumes the collection and returns its only element.
50 ///
51 /// This method panics with the given error function's return value if the collection does not
52 /// have exactly one element.
53 fn expect_element<Err: Display>(self, msg_fn: impl FnOnce() -> Err) -> T::Item;
54}
55
56impl<T> CollectionExt<T> for T
57where
58 T: IntoIterator,
59{
60 fn into_first(self) -> T::Item {
61 self.into_iter().next().unwrap()
62 }
63
64 fn into_last(self) -> T::Item {
65 self.into_iter().last().unwrap()
66 }
67
68 fn expect_element<Err: Display>(self, msg_fn: impl FnOnce() -> Err) -> T::Item {
69 let mut iter = self.into_iter();
70 match (iter.next(), iter.next()) {
71 (Some(el), None) => el,
72 _ => panic!("{}", msg_fn()),
73 }
74 }
75}
76
77/// Extension methods for associative collections.
78pub trait AssociativeExt<K, V> {
79 /// Inserts a key and value, panicking with
80 /// a given message if a true
81 /// insert (as opposed to an update) cannot be done
82 /// because the key already existed in the collection.
83 fn expect_insert(&mut self, k: K, v: V, msg: &str);
84 /// Inserts a key and value, panicking if a true
85 /// insert (as opposed to an update) cannot be done
86 /// because the key already existed in the collection.
87 fn unwrap_insert(&mut self, k: K, v: V) {
88 self.expect_insert(k, v, "called `unwrap_insert` for an already-existing key")
89 }
90
91 /// Removes a key, panicking with
92 /// a given message if a true
93 /// removal (as opposed to a no-op) cannot be done
94 /// because the key does not exist in the collection.
95 fn expect_remove(&mut self, k: &K, msg: &str) -> V;
96 /// Removes a key, panicking if a true
97 /// removal (as opposed to a no-op) cannot be done
98 /// because the key does not exist in the collection.
99 fn unwrap_remove(&mut self, k: &K) -> V {
100 self.expect_remove(k, "called `unwrap_remove` for a non-existing key")
101 }
102}
103
104impl<K, V> AssociativeExt<K, V> for HashMap<K, V>
105where
106 K: Eq + std::hash::Hash + Debug,
107 V: Debug,
108{
109 fn expect_insert(&mut self, k: K, v: V, msg: &str) {
110 match self.entry(k) {
111 HEntry::Vacant(e) => {
112 e.insert(v);
113 }
114 HEntry::Occupied(e) => {
115 panic!(
116 "{} (key: {:?}, old value: {:?}, new value: {:?})",
117 msg,
118 e.key(),
119 e.get(),
120 v
121 )
122 }
123 }
124 }
125
126 fn expect_remove(&mut self, k: &K, msg: &str) -> V {
127 match self.remove(k) {
128 Some(v) => v,
129 None => panic!("{} (key: {:?})", msg, k),
130 }
131 }
132}
133
134impl<K, V> AssociativeExt<K, V> for BTreeMap<K, V>
135where
136 K: Ord + Debug,
137 V: Debug,
138{
139 fn expect_insert(&mut self, k: K, v: V, msg: &str) {
140 match self.entry(k) {
141 BEntry::Vacant(e) => {
142 e.insert(v);
143 }
144 BEntry::Occupied(e) => {
145 panic!(
146 "{} (key: {:?}, old value: {:?}, new value: {:?})",
147 msg,
148 e.key(),
149 e.get(),
150 v
151 )
152 }
153 }
154 }
155
156 fn expect_remove(&mut self, k: &K, msg: &str) -> V {
157 match self.remove(k) {
158 Some(v) => v,
159 None => panic!("{} (key: {:?})", msg, k),
160 }
161 }
162}