1 // Copyright (C) 2023 The Android Open Source Project
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 at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! A mapping from crate names and versions to some associated data.
16
17 use std::collections::{BTreeMap, HashSet};
18
19 use itertools::Itertools;
20 use semver::Version;
21
22 use crate::{Error, NameAndVersion, NamedAndVersioned};
23
24 /// A mapping from crate names and versions to some associated data.
25 pub trait NameAndVersionMap {
26 /// The data associated with each crate name and version.
27 type Value;
28
29 /// Returns a reference to the map field.
map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>30 fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>;
31 /// Returns a mutable reference to the map field.
map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>32 fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>;
33 /// Tries to insert a new key and value, returning an error if the key is already present.
insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), Error>34 fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), Error>;
35 /// Returns the number of crates. Multiple versions of a crate count multiple times.
num_crates(&self) -> usize36 fn num_crates(&self) -> usize;
37 /// Returns true if the map contains a crate with the specified name.
contains_name(&self, name: &str) -> bool38 fn contains_name(&self, name: &str) -> bool {
39 self.get_versions(name).next().is_some()
40 }
41 /// Returns an iterator over versions of a specified crate
get_versions<'a, 'b>( &'a self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>42 fn get_versions<'a, 'b>(
43 &'a self,
44 name: &'b str,
45 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>;
46 /// Returns a mutable iterator over versions of a specified crate
get_versions_mut<'a, 'b>( &'a mut self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>47 fn get_versions_mut<'a, 'b>(
48 &'a mut self,
49 name: &'b str,
50 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>;
51 /// Returns an iterator over the map, filtered according to a predicate that acts on
52 /// all the available versions for a crate.
filter_versions< 'a: 'b, 'b, F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + 'a, >( &'a self, f: F, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>53 fn filter_versions<
54 'a: 'b,
55 'b,
56 F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version>
57 + 'a,
58 >(
59 &'a self,
60 f: F,
61 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>;
62 }
63
64 impl<ValueType> NameAndVersionMap for BTreeMap<NameAndVersion, ValueType> {
65 type Value = ValueType;
66
map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>67 fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> {
68 self
69 }
70
map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>71 fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> {
72 self
73 }
74
insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), Error>75 fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), Error> {
76 match self.entry(key) {
77 std::collections::btree_map::Entry::Vacant(e) => {
78 e.insert(val);
79 Ok(())
80 }
81 std::collections::btree_map::Entry::Occupied(e) => {
82 Err(Error::DuplicateVersion(e.key().name().to_string(), e.key().version().clone()))
83 }
84 }
85 }
86
num_crates(&self) -> usize87 fn num_crates(&self) -> usize {
88 let mut seen = ::std::collections::HashSet::new();
89 for nv in self.keys() {
90 seen.insert(nv.name().to_string());
91 }
92 seen.len()
93 }
94
get_versions<'a, 'b>( &'a self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>95 fn get_versions<'a, 'b>(
96 &'a self,
97 name: &'b str,
98 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> {
99 let owned_name = name.to_string();
100 Box::new(
101 self.range(std::ops::RangeFrom {
102 start: NameAndVersion::min_version(name.to_string()),
103 })
104 .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }),
105 )
106 }
107
get_versions_mut<'a, 'b>( &'a mut self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>108 fn get_versions_mut<'a, 'b>(
109 &'a mut self,
110 name: &'b str,
111 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> {
112 let owned_name = name.to_string();
113 Box::new(
114 self.range_mut(std::ops::RangeFrom {
115 start: NameAndVersion::min_version(name.to_string()),
116 })
117 .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }),
118 )
119 }
120
filter_versions< 'a: 'b, 'b, F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + 'a, >( &'a self, f: F, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>121 fn filter_versions<
122 'a: 'b,
123 'b,
124 F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version>
125 + 'a,
126 >(
127 &'a self,
128 f: F,
129 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> {
130 let mut kept_keys: HashSet<NameAndVersion> = HashSet::new();
131 for (key, mut group) in self.iter().chunk_by(|item| item.0.name()).into_iter() {
132 kept_keys.extend(
133 f(&mut group).into_iter().map(move |v| NameAndVersion::new(key.to_string(), v)),
134 );
135 }
136 Box::new(self.iter().filter(move |(nv, _krate)| kept_keys.contains(*nv)))
137 }
138 }
139
140 /// Select crates with a single version from an iterator.
crates_with_single_version<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>141 pub fn crates_with_single_version<'a, ValueType>(
142 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
143 ) -> HashSet<Version> {
144 let mut vset = HashSet::new();
145 versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count();
146 if vset.len() != 1 {
147 vset.clear()
148 }
149 vset
150 }
151
152 /// Select crates with multiple versions from an iterator.
crates_with_multiple_versions<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>153 pub fn crates_with_multiple_versions<'a, ValueType>(
154 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
155 ) -> HashSet<Version> {
156 let mut vset = HashSet::new();
157 versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count();
158 if vset.len() == 1 {
159 vset.clear()
160 }
161 vset
162 }
163
164 /// Select the most recent version of each crate from an iterator.
most_recent_version<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>165 pub fn most_recent_version<'a, ValueType>(
166 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
167 ) -> HashSet<Version> {
168 let mut vset = HashSet::new();
169 if let Some((nv, _crate)) = versions.into_iter().last() {
170 vset.insert(nv.version().clone());
171 }
172 vset
173 }
174
175 #[cfg(test)]
176 mod tests {
177 use crate::{NameAndVersion, NameAndVersionRef};
178
179 use super::*;
180 use itertools::assert_equal;
181
try_name_version_map_from_iter<'a, ValueType>( nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>, ) -> Result<BTreeMap<NameAndVersion, ValueType>, Error>182 fn try_name_version_map_from_iter<'a, ValueType>(
183 nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>,
184 ) -> Result<BTreeMap<NameAndVersion, ValueType>, Error> {
185 let mut test_map = BTreeMap::new();
186 for (name, version, val) in nvs {
187 test_map.insert_or_error(NameAndVersion::try_from_str(name, version)?, val)?;
188 }
189 Ok(test_map)
190 }
191
192 #[test]
test_name_and_version_map_empty() -> Result<(), Error>193 fn test_name_and_version_map_empty() -> Result<(), Error> {
194 let mut test_map: BTreeMap<NameAndVersion, String> = BTreeMap::new();
195 let v = Version::parse("1.2.3")?;
196 let nvp = NameAndVersionRef::new("foo", &v);
197 assert_eq!(test_map.num_crates(), 0);
198 assert!(!test_map.contains_key(&nvp as &dyn NamedAndVersioned));
199 assert!(!test_map.contains_name("foo"));
200 assert!(test_map.get_mut(&nvp as &dyn NamedAndVersioned).is_none());
201 Ok(())
202 }
203
204 #[test]
test_name_and_version_map_nonempty() -> Result<(), Error>205 fn test_name_and_version_map_nonempty() -> Result<(), Error> {
206 let mut test_map = try_name_version_map_from_iter([
207 ("foo", "1.2.3", "foo v1".to_string()),
208 ("foo", "2.3.4", "foo v2".to_string()),
209 ("bar", "1.0.0", "bar".to_string()),
210 ])?;
211
212 let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?;
213 let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?;
214 let bar = NameAndVersion::try_from_str("bar", "1.0.0")?;
215 let wrong_name = NameAndVersion::try_from_str("baz", "1.2.3")?;
216 let wrong_version = NameAndVersion::try_from_str("foo", "1.0.0")?;
217
218 assert_eq!(test_map.num_crates(), 2);
219
220 assert!(test_map.contains_key(&foo1));
221 assert!(test_map.contains_key(&foo2));
222 assert!(test_map.contains_key(&bar));
223 assert!(!test_map.contains_key(&wrong_name));
224 assert!(!test_map.contains_key(&wrong_version));
225
226 assert!(test_map.contains_name("foo"));
227 assert!(test_map.contains_name("bar"));
228 assert!(!test_map.contains_name("baz"));
229
230 assert_eq!(test_map.get(&foo1), Some(&"foo v1".to_string()));
231 assert_eq!(test_map.get(&foo2), Some(&"foo v2".to_string()));
232 assert_eq!(test_map.get(&bar), Some(&"bar".to_string()));
233 assert!(!test_map.contains_key(&wrong_name));
234 assert!(!test_map.contains_key(&wrong_version));
235
236 assert_eq!(test_map.get_mut(&foo1), Some(&mut "foo v1".to_string()));
237 assert_eq!(test_map.get_mut(&foo2), Some(&mut "foo v2".to_string()));
238 assert_eq!(test_map.get_mut(&bar), Some(&mut "bar".to_string()));
239 assert!(test_map.get_mut(&wrong_name).is_none());
240 assert!(test_map.get_mut(&wrong_version).is_none());
241
242 assert_equal(test_map.keys(), [&bar, &foo1, &foo2]);
243
244 assert_equal(test_map.values(), ["bar", "foo v1", "foo v2"]);
245 assert_equal(test_map.values_mut(), ["bar", "foo v1", "foo v2"]);
246
247 assert_equal(
248 test_map.iter().filter(|(_nv, x)| x.starts_with("foo")).map(|(_nv, val)| val),
249 ["foo v1", "foo v2"],
250 );
251
252 test_map.retain(|_nv, x| x.starts_with("foo"));
253 assert_equal(test_map.values(), ["foo v1", "foo v2"]);
254
255 Ok(())
256 }
257
258 #[test]
test_filter_versions() -> Result<(), Error>259 fn test_filter_versions() -> Result<(), Error> {
260 let test_map = try_name_version_map_from_iter([
261 ("foo", "1.2.3", ()),
262 ("foo", "2.3.4", ()),
263 ("bar", "1.0.0", ()),
264 ])?;
265 let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?;
266 let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?;
267 let bar = NameAndVersion::try_from_str("bar", "1.0.0")?;
268
269 assert_equal(
270 test_map.filter_versions(crates_with_single_version).map(|(nv, _)| nv),
271 [&bar],
272 );
273 assert_equal(
274 test_map.filter_versions(crates_with_multiple_versions).map(|(nv, _)| nv),
275 [&foo1, &foo2],
276 );
277 assert_equal(
278 test_map.filter_versions(most_recent_version).map(|(nv, _)| nv),
279 [&bar, &foo2],
280 );
281
282 Ok(())
283 }
284 }
285