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