xref: /aosp_15_r20/development/tools/external_crates/name_and_version/src/name_and_version_map.rs (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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