isr_dl_linux/ubuntu/
repository_cache.rs1use indexmap::IndexMap;
2use url::Url;
3
4use super::{
5 error::Error,
6 repository::{self, UbuntuRepositoryEntry},
7};
8
9pub struct UbuntuPackageCache {
10 host: Url,
11 packages: IndexMap<String, IndexMap<String, UbuntuRepositoryEntry>>,
12}
13
14impl UbuntuPackageCache {
15 pub fn fetch(
16 host: Url,
17 arch: &str,
18 dists: impl IntoIterator<Item = impl AsRef<str>>,
19 ) -> Result<Self, Error> {
20 let mut packages = IndexMap::<String, IndexMap<String, UbuntuRepositoryEntry>>::new();
21
22 for dist in dists {
23 let dist = dist.as_ref();
24
25 let repository = repository::fetch(host.clone(), arch, dist)?;
26 let packages = packages.entry(dist.to_owned()).or_default();
27
28 for entry in repository {
29 let package = match entry.package.as_deref() {
30 Some(package) => package,
31 None => continue,
33 };
34
35 packages.entry(package.into()).or_insert(entry);
36 }
37 }
38
39 Ok(Self { host, packages })
40 }
41
42 pub fn find_package(
43 &self,
44 package: &str,
45 version: &str,
46 ) -> Result<Option<&UbuntuRepositoryEntry>, Error> {
47 tracing::info!(package, version, "finding package");
48 self.find(package, version, false)
49 }
50
51 pub fn find_dbgsym_package(
52 &self,
53 package: &str,
54 version: &str,
55 ) -> Result<Option<&UbuntuRepositoryEntry>, Error> {
56 tracing::info!(package, version, "finding dbgsym package");
57 self.find(package, version, true)
58 }
59
60 pub fn package_url(&self, entry: &UbuntuRepositoryEntry) -> Result<Url, Error> {
61 match &entry.filename {
62 Some(filename) => Ok(self.host.join(filename)?),
63 None => Err(Error::PackageMissingFilename),
64 }
65 }
66
67 fn find(
68 &self,
69 package: &str,
70 version: &str,
71 dbgsym: bool,
72 ) -> Result<Option<&UbuntuRepositoryEntry>, Error> {
73 let mut candidates = Vec::new();
74
75 for (dist, packages) in &self.packages {
76 let entry = match packages.get(package) {
77 Some(entry) => entry,
78 None => continue,
79 };
80
81 let entry_version = match &entry.version {
82 Some(entry_version) => entry_version,
83 None => continue,
84 };
85
86 if entry_version != version {
87 continue;
88 }
89
90 if dbgsym {
91 if entry.depends.is_some() {
102 continue;
103 }
104 };
105
106 candidates.push((dist.as_str(), entry));
112 }
113
114 let candidate = match candidates.pop() {
115 Some(candidate) => candidate,
116 None => return Ok(None),
117 };
118
119 if !candidates.is_empty() {
120 let dists = std::iter::once(candidate.0)
121 .chain(candidates.into_iter().map(|(dist, _)| dist))
122 .collect::<Vec<_>>();
123
124 tracing::error!(?dists, "multiple candidates found");
125 return Err(Error::PackageMultipleCandidates);
126 }
127
128 Ok(Some(candidate.1))
129 }
130}