rustup_available_packages/
availability.rs1use crate::manifest::Manifest;
4use chrono::NaiveDate;
5use std::{
6 borrow::Borrow,
7 collections::{HashMap, HashSet},
8};
9
10type PackageName = String;
11type TargetTriple = String;
12type DatesSet = HashSet<NaiveDate>;
13type PackagesAvailability = HashMap<PackageName, DatesSet>;
14
15#[derive(Debug, Default)]
17pub struct AvailabilityData {
18 data: HashMap<TargetTriple, PackagesAvailability>,
19}
20
21#[derive(Debug, serde::Serialize)]
23pub struct AvailabilityRow<'a> {
24 pub package_name: &'a str,
26 pub availability_list: Vec<bool>,
28 pub last_available: Option<NaiveDate>,
30 _hidden: (),
32}
33
34impl AvailabilityData {
35 pub fn add_manifest(&mut self, manifest: Manifest) {
37 let reverse_renames: HashMap<_, _> = manifest
38 .renames
39 .iter()
40 .map(|(key, value)| (&value.to, key))
41 .collect();
42 for (package_name, info) in manifest.packages {
43 let package_name = reverse_renames
44 .get(&package_name)
45 .map(|name| String::clone(name))
46 .unwrap_or(package_name);
47 for (target_triple, target_info) in info.targets {
48 if target_info.available {
49 self.data
50 .entry(target_triple.clone())
51 .or_default()
52 .entry(package_name.clone())
53 .or_default()
54 .insert(manifest.date);
55 }
56 }
57 }
58 }
59
60 pub fn add_manifests(&mut self, manifests: impl IntoIterator<Item = Manifest>) {
62 manifests
63 .into_iter()
64 .for_each(|manifest| self.add_manifest(manifest));
65 }
66
67 pub fn get_available_targets(&self) -> HashSet<&'_ str> {
70 self.data
71 .keys()
72 .filter(|target| target != &"*")
73 .map(AsRef::as_ref)
74 .collect()
75 }
76
77 pub fn get_available_packages<'a>(&'a self) -> HashSet<&'a str> {
79 self.data
80 .iter()
81 .flat_map(|(_, per_target)| per_target.keys())
82 .map(AsRef::as_ref)
83 .collect()
84 }
85
86 pub fn get_availability_row<'a, I>(
91 &self,
92 target: &str,
93 pkg: &'a str,
94 dates: I,
95 ) -> Option<AvailabilityRow<'a>>
96 where
97 I: IntoIterator,
98 I::Item: Borrow<NaiveDate>,
99 {
100 if self.data.get(target).and_then(|t| t.get(pkg)).is_none() {
101 return None;
102 }
103 let available_dates = self.available_dates(target, pkg);
104 let availability_list = dates
105 .into_iter()
106 .map(|date| available_dates.contains(date.borrow()))
107 .collect();
108 Some(AvailabilityRow {
109 package_name: pkg,
110 availability_list,
111 last_available: available_dates.into_iter().max(),
112 _hidden: (),
113 })
114 }
115
116 fn available_dates(&self, target: &str, pkg: &str) -> HashSet<NaiveDate> {
118 let available_on_target = self.data.get(target).and_then(|packages| packages.get(pkg));
119 let available_on_wildcard = self.data.get("*").and_then(|packages| packages.get(pkg));
120 match (available_on_target, available_on_wildcard) {
122 (Some(x), Some(y)) => x.union(y).cloned().collect(),
123 (Some(x), None) | (None, Some(x)) => x.iter().cloned().collect(),
124 (None, None) => HashSet::new(),
125 }
126 }
127
128 pub fn last_available(&self, target: &str, pkg: &str) -> Option<NaiveDate> {
130 self.available_dates(target, pkg).into_iter().max()
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::manifest::Manifest;
138
139 #[test]
140 fn check() {
141 let data = r#"date = "2018-09-03"
142[pkg.rust-src.target."*"]
143available = true
144[pkg.ahaha.target.lol]
145available = true
146"#;
147 let parsed_manifest: Manifest = toml::from_str(data).unwrap();
148 let mut availability: AvailabilityData = Default::default();
149 availability.add_manifest(parsed_manifest);
150 let all_packages = availability.get_available_packages();
151 assert_eq!(2, all_packages.len());
152 assert!(all_packages.contains("rust-src"));
153 assert!(all_packages.contains("ahaha"));
154
155 let all_targets = availability.get_available_targets();
156 assert_eq!(1, all_targets.len());
158 assert!(all_targets.contains("lol"));
159
160 let package_exists = availability
161 .get_availability_row("*", "rust-src", vec![NaiveDate::from_ymd(2018, 9, 3)])
162 .unwrap();
163 assert_eq!("rust-src", package_exists.package_name);
164 assert_eq!(vec!(true), package_exists.availability_list);
165 let package_exists = availability.get_availability_row(
166 "lol",
167 "rust-src",
168 vec![NaiveDate::from_ymd(2018, 9, 3)],
169 );
170 assert!(package_exists.is_none());
172 let package_exists = availability
173 .get_availability_row("lol", "ahaha", vec![NaiveDate::from_ymd(2018, 9, 3)])
174 .unwrap();
175 assert_eq!("ahaha", package_exists.package_name);
176 assert_eq!(vec!(true), package_exists.availability_list);
177 }
178
179 #[test]
180 fn check_rename() {
181 let data = r#"date = "2018-09-03"
182[pkg.ahaha.target.lol]
183available = true
184[renames.kek]
185to = "ahaha"
186"#;
187 let parsed_manifest: Manifest = toml::from_str(data).unwrap();
188 let mut availability: AvailabilityData = Default::default();
189 availability.add_manifest(parsed_manifest);
190 let all_packages = availability.get_available_packages();
191 assert_eq!(1, all_packages.len());
192 assert!(all_packages.contains("kek"));
193 }
194}