diff --git a/src/lib.rs b/src/lib.rs index 498b5907..cc1c943f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,6 +212,7 @@ mod error; mod package; +mod provider; mod report; mod solver; mod term; @@ -221,11 +222,12 @@ mod version_set; pub use error::{NoSolutionError, PubGrubError}; pub use package::Package; +pub use provider::OfflineDependencyProvider; pub use report::{ DefaultStringReportFormatter, DefaultStringReporter, DerivationTree, Derived, External, ReportFormatter, Reporter, }; -pub use solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider}; +pub use solver::{resolve, Dependencies, DependencyProvider}; pub use term::Term; pub use type_aliases::{DependencyConstraints, Map, SelectedDependencies, Set}; pub use version::{SemanticVersion, VersionParseError}; diff --git a/src/provider.rs b/src/provider.rs new file mode 100644 index 00000000..83268795 --- /dev/null +++ b/src/provider.rs @@ -0,0 +1,120 @@ +use std::cmp::Reverse; +use std::collections::BTreeMap; +use std::convert::Infallible; + +use crate::{Dependencies, DependencyConstraints, DependencyProvider, Map, Package, VersionSet}; + +/// A basic implementation of [DependencyProvider]. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "serde", + serde(bound( + serialize = "VS::V: serde::Serialize, VS: serde::Serialize, P: serde::Serialize", + deserialize = "VS::V: serde::Deserialize<'de>, VS: serde::Deserialize<'de>, P: serde::Deserialize<'de>" + )) +)] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct OfflineDependencyProvider { + dependencies: Map>>, +} + +impl OfflineDependencyProvider { + /// Creates an empty OfflineDependencyProvider with no dependencies. + pub fn new() -> Self { + Self { + dependencies: Map::default(), + } + } + + /// Registers the dependencies of a package and version pair. + /// Dependencies must be added with a single call to + /// [add_dependencies](OfflineDependencyProvider::add_dependencies). + /// All subsequent calls to + /// [add_dependencies](OfflineDependencyProvider::add_dependencies) for a given + /// package version pair will replace the dependencies by the new ones. + /// + /// The API does not allow to add dependencies one at a time to uphold an assumption that + /// [OfflineDependencyProvider.get_dependencies(p, v)](OfflineDependencyProvider::get_dependencies) + /// provides all dependencies of a given package (p) and version (v) pair. + pub fn add_dependencies>( + &mut self, + package: P, + version: impl Into, + dependencies: I, + ) { + let package_deps = dependencies.into_iter().collect(); + let v = version.into(); + *self + .dependencies + .entry(package) + .or_default() + .entry(v) + .or_default() = package_deps; + } + + /// Lists packages that have been saved. + pub fn packages(&self) -> impl Iterator { + self.dependencies.keys() + } + + /// Lists versions of saved packages in sorted order. + /// Returns [None] if no information is available regarding that package. + pub fn versions(&self, package: &P) -> Option> { + self.dependencies.get(package).map(|k| k.keys()) + } + + /// Lists dependencies of a given package and version. + /// Returns [None] if no information is available regarding that package and version pair. + fn dependencies(&self, package: &P, version: &VS::V) -> Option> { + self.dependencies.get(package)?.get(version).cloned() + } +} + +/// An implementation of [DependencyProvider] that +/// contains all dependency information available in memory. +/// Currently packages are picked with the fewest versions contained in the constraints first. +/// But, that may change in new versions if better heuristics are found. +/// Versions are picked with the newest versions first. +impl DependencyProvider for OfflineDependencyProvider { + type P = P; + type V = VS::V; + type VS = VS; + type M = String; + + type Err = Infallible; + + #[inline] + fn choose_version(&self, package: &P, range: &VS) -> Result, Infallible> { + Ok(self + .dependencies + .get(package) + .and_then(|versions| versions.keys().rev().find(|v| range.contains(v)).cloned())) + } + + type Priority = Reverse; + + #[inline] + fn prioritize(&self, package: &P, range: &VS) -> Self::Priority { + Reverse( + self.dependencies + .get(package) + .map(|versions| versions.keys().filter(|v| range.contains(v)).count()) + .unwrap_or(0), + ) + } + + #[inline] + fn get_dependencies( + &self, + package: &P, + version: &VS::V, + ) -> Result, Infallible> { + Ok(match self.dependencies(package, version) { + None => { + Dependencies::Unavailable("its dependencies could not be determined".to_string()) + } + Some(dependencies) => Dependencies::Available(dependencies), + }) + } +} diff --git a/src/solver.rs b/src/solver.rs index 2abc2e37..0f61fbf4 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -59,9 +59,7 @@ //! to satisfy the dependencies of that package and version pair. //! If there is no solution, the reason will be provided as clear as possible. -use std::cmp::Reverse; -use std::collections::{BTreeMap, BTreeSet as Set}; -use std::convert::Infallible; +use std::collections::BTreeSet as Set; use std::error::Error; use std::fmt::{Debug, Display}; @@ -244,7 +242,7 @@ pub trait DependencyProvider { /// The type returned from `prioritize`. The resolver does not care what type this is /// as long as it can pick a largest one and clone it. /// - /// [Reverse] can be useful if you want to pick the package with + /// [`Reverse`](std::cmp::Reverse) can be useful if you want to pick the package with /// the fewest versions that match the outstanding constraint. type Priority: Ord + Clone; @@ -280,114 +278,3 @@ pub trait DependencyProvider { Ok(()) } } - -/// A basic implementation of [DependencyProvider]. -#[derive(Debug, Clone, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr( - feature = "serde", - serde(bound( - serialize = "VS::V: serde::Serialize, VS: serde::Serialize, P: serde::Serialize", - deserialize = "VS::V: serde::Deserialize<'de>, VS: serde::Deserialize<'de>, P: serde::Deserialize<'de>" - )) -)] -#[cfg_attr(feature = "serde", serde(transparent))] -pub struct OfflineDependencyProvider { - dependencies: Map>>, -} - -impl OfflineDependencyProvider { - /// Creates an empty OfflineDependencyProvider with no dependencies. - pub fn new() -> Self { - Self { - dependencies: Map::default(), - } - } - - /// Registers the dependencies of a package and version pair. - /// Dependencies must be added with a single call to - /// [add_dependencies](OfflineDependencyProvider::add_dependencies). - /// All subsequent calls to - /// [add_dependencies](OfflineDependencyProvider::add_dependencies) for a given - /// package version pair will replace the dependencies by the new ones. - /// - /// The API does not allow to add dependencies one at a time to uphold an assumption that - /// [OfflineDependencyProvider.get_dependencies(p, v)](OfflineDependencyProvider::get_dependencies) - /// provides all dependencies of a given package (p) and version (v) pair. - pub fn add_dependencies>( - &mut self, - package: P, - version: impl Into, - dependencies: I, - ) { - let package_deps = dependencies.into_iter().collect(); - let v = version.into(); - *self - .dependencies - .entry(package) - .or_default() - .entry(v) - .or_default() = package_deps; - } - - /// Lists packages that have been saved. - pub fn packages(&self) -> impl Iterator { - self.dependencies.keys() - } - - /// Lists versions of saved packages in sorted order. - /// Returns [None] if no information is available regarding that package. - pub fn versions(&self, package: &P) -> Option> { - self.dependencies.get(package).map(|k| k.keys()) - } - - /// Lists dependencies of a given package and version. - /// Returns [None] if no information is available regarding that package and version pair. - fn dependencies(&self, package: &P, version: &VS::V) -> Option> { - self.dependencies.get(package)?.get(version).cloned() - } -} - -/// An implementation of [DependencyProvider] that -/// contains all dependency information available in memory. -/// Currently packages are picked with the fewest versions contained in the constraints first. -/// But, that may change in new versions if better heuristics are found. -/// Versions are picked with the newest versions first. -impl DependencyProvider for OfflineDependencyProvider { - type P = P; - type V = VS::V; - type VS = VS; - type M = String; - - type Err = Infallible; - - fn choose_version(&self, package: &P, range: &VS) -> Result, Infallible> { - Ok(self - .dependencies - .get(package) - .and_then(|versions| versions.keys().rev().find(|v| range.contains(v)).cloned())) - } - - type Priority = Reverse; - fn prioritize(&self, package: &P, range: &VS) -> Self::Priority { - Reverse( - self.dependencies - .get(package) - .map(|versions| versions.keys().filter(|v| range.contains(v)).count()) - .unwrap_or(0), - ) - } - - fn get_dependencies( - &self, - package: &P, - version: &VS::V, - ) -> Result, Infallible> { - Ok(match self.dependencies(package, version) { - None => { - Dependencies::Unavailable("its dependencies could not be determined".to_string()) - } - Some(dependencies) => Dependencies::Available(dependencies), - }) - } -}