isr_dl_linux/ubuntu/
repository_cache.rs

1use 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                    // Ignore packages without a name.
32                    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                //
92                // Some dbgsym packages have dependencies on the main package.
93                // For example:
94                //    Package: linux-image-6.8.0-40-generic-dbgsym
95                //    Depends: linux-image-unsigned-6.8.0-40-generic-dbgsym
96                //    Size: 20796
97                //
98                // The main dbgsym package should not depend on anything.
99                //
100
101                if entry.depends.is_some() {
102                    continue;
103                }
104            };
105
106            // let entry_filename = match &entry.filename {
107            //     Some(entry_filename) => entry_filename,
108            //     None => continue,
109            // };
110
111            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}