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 use std::collections::{BTreeMap, HashSet};
16
17 use anyhow::Result;
18 use itertools::Itertools;
19 use semver::Version;
20
21 use crate::{CrateError, IsUpgradableTo, NameAndVersion, NamedAndVersioned};
22
23 pub trait NameAndVersionMap {
24 type Value;
25
map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>26 fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>;
map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>27 fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>;
28
insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError>29 fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError>;
num_crates(&self) -> usize30 fn num_crates(&self) -> usize;
contains_name(&self, name: &str) -> bool31 fn contains_name(&self, name: &str) -> bool {
32 self.get_versions(name).next().is_some()
33 }
get_versions<'a, 'b>( &'a self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>34 fn get_versions<'a, 'b>(
35 &'a self,
36 name: &'b str,
37 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>;
get_versions_mut<'a, 'b>( &'a mut self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>38 fn get_versions_mut<'a, 'b>(
39 &'a mut self,
40 name: &'b str,
41 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>;
get_version_upgradable_from<T: NamedAndVersioned + IsUpgradableTo>( &self, other: &T, ) -> Option<&NameAndVersion>42 fn get_version_upgradable_from<T: NamedAndVersioned + IsUpgradableTo>(
43 &self,
44 other: &T,
45 ) -> Option<&NameAndVersion> {
46 let mut best_version = None;
47 for (nv, _val) in self.get_versions(other.name()) {
48 if other.is_upgradable_to(nv) {
49 best_version.replace(nv);
50 }
51 }
52 best_version
53 }
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>54 fn filter_versions<
55 'a: 'b,
56 'b,
57 F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version>
58 + 'a,
59 >(
60 &'a self,
61 f: F,
62 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>;
63 }
64
65 impl<ValueType> NameAndVersionMap for BTreeMap<NameAndVersion, ValueType> {
66 type Value = ValueType;
67
map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>68 fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> {
69 self
70 }
71
map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>72 fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> {
73 self
74 }
75
insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError>76 fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError> {
77 if self.contains_key(&key) {
78 Err(CrateError::DuplicateCrateVersion(key.name().to_string(), key.version().clone()))
79 } else {
80 self.insert(key, val);
81 Ok(())
82 }
83 }
84
num_crates(&self) -> usize85 fn num_crates(&self) -> usize {
86 let mut seen = ::std::collections::HashSet::new();
87 for nv in self.keys() {
88 seen.insert(nv.name().to_string());
89 }
90 seen.len()
91 }
92
get_versions<'a, 'b>( &'a self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>93 fn get_versions<'a, 'b>(
94 &'a self,
95 name: &'b str,
96 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> {
97 let owned_name = name.to_string();
98 Box::new(
99 self.range(std::ops::RangeFrom {
100 start: NameAndVersion::min_version(name.to_string()),
101 })
102 .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }),
103 )
104 }
105
get_versions_mut<'a, 'b>( &'a mut self, name: &'b str, ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>106 fn get_versions_mut<'a, 'b>(
107 &'a mut self,
108 name: &'b str,
109 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> {
110 let owned_name = name.to_string();
111 Box::new(
112 self.range_mut(std::ops::RangeFrom {
113 start: NameAndVersion::min_version(name.to_string()),
114 })
115 .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }),
116 )
117 }
118
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>119 fn filter_versions<
120 'a: 'b,
121 'b,
122 F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version>
123 + 'a,
124 >(
125 &'a self,
126 f: F,
127 ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> {
128 let mut kept_keys: HashSet<NameAndVersion> = HashSet::new();
129 for (key, mut group) in self.iter().group_by(|item| item.0.name()).into_iter() {
130 kept_keys.extend(
131 f(&mut group).into_iter().map(move |v| NameAndVersion::new(key.to_string(), v)),
132 );
133 }
134 Box::new(self.iter().filter(move |(nv, _krate)| kept_keys.contains(*nv)))
135 }
136 }
137
crates_with_single_version<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>138 pub fn crates_with_single_version<'a, ValueType>(
139 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
140 ) -> HashSet<Version> {
141 let mut vset = HashSet::new();
142 versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count();
143 if vset.len() != 1 {
144 vset.clear()
145 }
146 vset
147 }
148
crates_with_multiple_versions<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>149 pub fn crates_with_multiple_versions<'a, ValueType>(
150 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
151 ) -> HashSet<Version> {
152 let mut vset = HashSet::new();
153 versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count();
154 if vset.len() == 1 {
155 vset.clear()
156 }
157 vset
158 }
159
most_recent_version<'a, ValueType>( versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, ) -> HashSet<Version>160 pub fn most_recent_version<'a, ValueType>(
161 versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>,
162 ) -> HashSet<Version> {
163 let mut vset = HashSet::new();
164 if let Some((nv, _crate)) = versions.into_iter().last() {
165 vset.insert(nv.version().clone());
166 }
167 vset
168 }
169
170 #[cfg(test)]
try_name_version_map_from_iter<'a, ValueType>( nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>, ) -> Result<BTreeMap<NameAndVersion, ValueType>>171 pub fn try_name_version_map_from_iter<'a, ValueType>(
172 nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>,
173 ) -> Result<BTreeMap<NameAndVersion, ValueType>> {
174 let mut test_map = BTreeMap::new();
175 for (name, version, val) in nvs {
176 test_map.insert_or_error(NameAndVersion::try_from_str(name, version)?, val)?;
177 }
178 Ok(test_map)
179 }
180
181 #[cfg(test)]
182 mod tests {
183 use crate::{NameAndVersion, NameAndVersionRef};
184
185 use super::*;
186 use anyhow::Result;
187 use itertools::assert_equal;
188
189 #[test]
test_name_and_version_map_empty() -> Result<()>190 fn test_name_and_version_map_empty() -> Result<()> {
191 let mut test_map: BTreeMap<NameAndVersion, String> = BTreeMap::new();
192 let v = Version::parse("1.2.3")?;
193 let nvp = NameAndVersionRef::new("foo", &v);
194 // let nvp = NameAndVersion::try_from_str("foo", "1.2.3")?;
195 assert_eq!(test_map.num_crates(), 0);
196 assert!(!test_map.contains_key(&nvp as &dyn NamedAndVersioned));
197 assert!(!test_map.contains_name("foo"));
198 assert!(test_map.get(&nvp as &dyn NamedAndVersioned).is_none());
199 assert!(test_map.get_mut(&nvp as &dyn NamedAndVersioned).is_none());
200 Ok(())
201 }
202
203 #[test]
test_name_and_version_map_nonempty() -> Result<()>204 fn test_name_and_version_map_nonempty() -> Result<()> {
205 let mut test_map = try_name_version_map_from_iter([
206 ("foo", "1.2.3", "foo v1".to_string()),
207 ("foo", "2.3.4", "foo v2".to_string()),
208 ("bar", "1.0.0", "bar".to_string()),
209 ])?;
210
211 let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?;
212 let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?;
213 let bar = NameAndVersion::try_from_str("bar", "1.0.0")?;
214 let wrong_name = NameAndVersion::try_from_str("baz", "1.2.3")?;
215 let wrong_version = NameAndVersion::try_from_str("foo", "1.0.0")?;
216
217 assert_eq!(test_map.num_crates(), 2);
218
219 assert!(test_map.contains_key(&foo1));
220 assert!(test_map.contains_key(&foo2));
221 assert!(test_map.contains_key(&bar));
222 assert!(!test_map.contains_key(&wrong_name));
223 assert!(!test_map.contains_key(&wrong_version));
224
225 assert!(test_map.contains_name("foo"));
226 assert!(test_map.contains_name("bar"));
227 assert!(!test_map.contains_name("baz"));
228
229 assert_eq!(test_map.get(&foo1), Some(&"foo v1".to_string()));
230 assert_eq!(test_map.get(&foo2), Some(&"foo v2".to_string()));
231 assert_eq!(test_map.get(&bar), Some(&"bar".to_string()));
232 assert!(test_map.get(&wrong_name).is_none());
233 assert!(test_map.get(&wrong_version).is_none());
234
235 assert_eq!(test_map.get_mut(&foo1), Some(&mut "foo v1".to_string()));
236 assert_eq!(test_map.get_mut(&foo2), Some(&mut "foo v2".to_string()));
237 assert_eq!(test_map.get_mut(&bar), Some(&mut "bar".to_string()));
238 assert!(test_map.get_mut(&wrong_name).is_none());
239 assert!(test_map.get_mut(&wrong_version).is_none());
240
241 assert_eq!(
242 test_map.get_version_upgradable_from(&NameAndVersion::try_from_str("foo", "1.2.2")?),
243 Some(&foo1)
244 );
245
246 // TOOD: Iter
247 assert_equal(test_map.keys(), [&bar, &foo1, &foo2]);
248
249 assert_equal(test_map.values(), ["bar", "foo v1", "foo v2"]);
250 assert_equal(test_map.values_mut(), ["bar", "foo v1", "foo v2"]);
251
252 assert_equal(
253 test_map.iter().filter(|(_nv, x)| x.starts_with("foo")).map(|(_nv, val)| val),
254 ["foo v1", "foo v2"],
255 );
256
257 test_map.retain(|_nv, x| x.starts_with("foo"));
258 assert_equal(test_map.values(), ["foo v1", "foo v2"]);
259
260 Ok(())
261 }
262
263 #[test]
test_filter_versions() -> Result<()>264 fn test_filter_versions() -> Result<()> {
265 let test_map = try_name_version_map_from_iter([
266 ("foo", "1.2.3", ()),
267 ("foo", "2.3.4", ()),
268 ("bar", "1.0.0", ()),
269 ])?;
270 let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?;
271 let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?;
272 let bar = NameAndVersion::try_from_str("bar", "1.0.0")?;
273
274 assert_equal(
275 test_map.filter_versions(crates_with_single_version).map(|(nv, _)| nv),
276 [&bar],
277 );
278 assert_equal(
279 test_map.filter_versions(crates_with_multiple_versions).map(|(nv, _)| nv),
280 [&foo1, &foo2],
281 );
282 assert_equal(
283 test_map.filter_versions(most_recent_version).map(|(nv, _)| nv),
284 [&bar, &foo2],
285 );
286
287 Ok(())
288 }
289 }
290