pubgrub/
provider.rs

1use std::cmp::Reverse;
2use std::collections::BTreeMap;
3use std::convert::Infallible;
4
5use crate::{
6    Dependencies, DependencyConstraints, DependencyProvider, Map, Package,
7    PackageResolutionStatistics, VersionSet,
8};
9
10/// A basic implementation of [DependencyProvider].
11#[derive(Debug, Clone, Default)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(
14    feature = "serde",
15    serde(bound(
16        serialize = "VS::V: serde::Serialize, VS: serde::Serialize, P: serde::Serialize",
17        deserialize = "VS::V: serde::Deserialize<'de>, VS: serde::Deserialize<'de>, P: serde::Deserialize<'de>"
18    ))
19)]
20#[cfg_attr(feature = "serde", serde(transparent))]
21pub struct OfflineDependencyProvider<P: Package, VS: VersionSet> {
22    dependencies: Map<P, BTreeMap<VS::V, DependencyConstraints<P, VS>>>,
23}
24
25impl<P: Package, VS: VersionSet> OfflineDependencyProvider<P, VS> {
26    /// Creates an empty OfflineDependencyProvider with no dependencies.
27    pub fn new() -> Self {
28        Self {
29            dependencies: Map::default(),
30        }
31    }
32
33    /// Registers the dependencies of a package and version pair.
34    /// Dependencies must be added with a single call to
35    /// [add_dependencies](OfflineDependencyProvider::add_dependencies).
36    /// All subsequent calls to
37    /// [add_dependencies](OfflineDependencyProvider::add_dependencies) for a given
38    /// package version pair will replace the dependencies by the new ones.
39    ///
40    /// The API does not allow to add dependencies one at a time to uphold an assumption that
41    /// [OfflineDependencyProvider.get_dependencies(p, v)](OfflineDependencyProvider::get_dependencies)
42    /// provides all dependencies of a given package (p) and version (v) pair.
43    pub fn add_dependencies<I: IntoIterator<Item = (P, VS)>>(
44        &mut self,
45        package: P,
46        version: impl Into<VS::V>,
47        dependencies: I,
48    ) {
49        let package_deps = dependencies.into_iter().collect();
50        let v = version.into();
51        *self
52            .dependencies
53            .entry(package)
54            .or_default()
55            .entry(v)
56            .or_default() = package_deps;
57    }
58
59    /// Lists packages that have been saved.
60    pub fn packages(&self) -> impl Iterator<Item = &P> {
61        self.dependencies.keys()
62    }
63
64    /// Lists versions of saved packages in sorted order.
65    /// Returns [None] if no information is available regarding that package.
66    pub fn versions(&self, package: &P) -> Option<impl Iterator<Item = &VS::V>> {
67        self.dependencies.get(package).map(|k| k.keys())
68    }
69
70    /// Lists dependencies of a given package and version.
71    /// Returns [None] if no information is available regarding that package and version pair.
72    fn dependencies(&self, package: &P, version: &VS::V) -> Option<DependencyConstraints<P, VS>> {
73        self.dependencies.get(package)?.get(version).cloned()
74    }
75}
76
77/// An implementation of [DependencyProvider] that
78/// contains all dependency information available in memory.
79/// Currently packages are picked with the fewest versions contained in the constraints first.
80/// But, that may change in new versions if better heuristics are found.
81/// Versions are picked with the newest versions first.
82impl<P: Package, VS: VersionSet> DependencyProvider for OfflineDependencyProvider<P, VS> {
83    type P = P;
84    type V = VS::V;
85    type VS = VS;
86    type M = String;
87
88    type Err = Infallible;
89
90    #[inline]
91    fn choose_version(&self, package: &P, range: &VS) -> Result<Option<VS::V>, Infallible> {
92        Ok(self
93            .dependencies
94            .get(package)
95            .and_then(|versions| versions.keys().rev().find(|v| range.contains(v)).cloned()))
96    }
97
98    type Priority = (u32, Reverse<usize>);
99
100    #[inline]
101    fn prioritize(
102        &self,
103        package: &Self::P,
104        range: &Self::VS,
105        package_statistics: &PackageResolutionStatistics,
106    ) -> Self::Priority {
107        let version_count = self
108            .dependencies
109            .get(package)
110            .map(|versions| versions.keys().filter(|v| range.contains(v)).count())
111            .unwrap_or(0);
112        if version_count == 0 {
113            return (u32::MAX, Reverse(0));
114        }
115        (package_statistics.conflict_count(), Reverse(version_count))
116    }
117
118    #[inline]
119    fn get_dependencies(
120        &self,
121        package: &P,
122        version: &VS::V,
123    ) -> Result<Dependencies<P, VS, Self::M>, Infallible> {
124        Ok(match self.dependencies(package, version) {
125            None => {
126                Dependencies::Unavailable("its dependencies could not be determined".to_string())
127            }
128            Some(dependencies) => Dependencies::Available(dependencies),
129        })
130    }
131}