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    #[track_caller]
46    fn into_element(self) -> T::Item {
47        self.expect_element(|| "into_element called on collection without exactly one element")
48    }
49
50    /// Consumes the collection and returns its only element.
51    ///
52    /// This method panics with the given error function's return value if the collection does not
53    /// have exactly one element.
54    fn expect_element<Err: Display>(self, msg_fn: impl FnOnce() -> Err) -> T::Item;
55}
56
57impl<T> CollectionExt<T> for T
58where
59    T: IntoIterator,
60{
61    #[track_caller]
62    fn into_first(self) -> T::Item {
63        self.into_iter().next().unwrap()
64    }
65
66    #[track_caller]
67    fn into_last(self) -> T::Item {
68        self.into_iter().last().unwrap()
69    }
70
71    #[track_caller]
72    fn expect_element<Err: Display>(self, msg_fn: impl FnOnce() -> Err) -> T::Item {
73        let mut iter = self.into_iter();
74        match (iter.next(), iter.next()) {
75            (Some(el), None) => el,
76            _ => panic!("{}", msg_fn()),
77        }
78    }
79}
80
81/// Extension methods for associative collections.
82pub trait AssociativeExt<K, V> {
83    /// Inserts a key and value, panicking with
84    /// a given message if a true
85    /// insert (as opposed to an update) cannot be done
86    /// because the key already existed in the collection.
87    fn expect_insert(&mut self, k: K, v: V, msg: &str);
88    /// Inserts a key and value, panicking if a true
89    /// insert (as opposed to an update) cannot be done
90    /// because the key already existed in the collection.
91    fn unwrap_insert(&mut self, k: K, v: V) {
92        self.expect_insert(k, v, "called `unwrap_insert` for an already-existing key")
93    }
94
95    /// Removes a key, panicking with
96    /// a given message 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 expect_remove(&mut self, k: &K, msg: &str) -> V;
100    /// Removes a key, panicking if a true
101    /// removal (as opposed to a no-op) cannot be done
102    /// because the key does not exist in the collection.
103    fn unwrap_remove(&mut self, k: &K) -> V {
104        self.expect_remove(k, "called `unwrap_remove` for a non-existing key")
105    }
106}
107
108impl<K, V> AssociativeExt<K, V> for HashMap<K, V>
109where
110    K: Eq + std::hash::Hash + Debug,
111    V: Debug,
112{
113    fn expect_insert(&mut self, k: K, v: V, msg: &str) {
114        match self.entry(k) {
115            HEntry::Vacant(e) => {
116                e.insert(v);
117            }
118            HEntry::Occupied(e) => {
119                panic!(
120                    "{} (key: {:?}, old value: {:?}, new value: {:?})",
121                    msg,
122                    e.key(),
123                    e.get(),
124                    v
125                )
126            }
127        }
128    }
129
130    fn expect_remove(&mut self, k: &K, msg: &str) -> V {
131        match self.remove(k) {
132            Some(v) => v,
133            None => panic!("{} (key: {:?})", msg, k),
134        }
135    }
136}
137
138impl<K, V> AssociativeExt<K, V> for BTreeMap<K, V>
139where
140    K: Ord + Debug,
141    V: Debug,
142{
143    fn expect_insert(&mut self, k: K, v: V, msg: &str) {
144        match self.entry(k) {
145            BEntry::Vacant(e) => {
146                e.insert(v);
147            }
148            BEntry::Occupied(e) => {
149                panic!(
150                    "{} (key: {:?}, old value: {:?}, new value: {:?})",
151                    msg,
152                    e.key(),
153                    e.get(),
154                    v
155                )
156            }
157        }
158    }
159
160    fn expect_remove(&mut self, k: &K, msg: &str) -> V {
161        match self.remove(k) {
162            Some(v) => v,
163            None => panic!("{} (key: {:?})", msg, k),
164        }
165    }
166}