resolvo/
lib.rs

1//! Implements a SAT solver for dependency resolution based on the CDCL
2//! algorithm (conflict-driven clause learning)
3//!
4//! The CDCL algorithm is masterly explained in [An Extensible
5//! SAT-solver](http://minisat.se/downloads/MiniSat.pdf). Regarding the data structures used, we
6//! mostly follow the approach taken by [libsolv](https://github.com/openSUSE/libsolv). The code of
7//! libsolv is, however, very low level C, so if you are looking for an
8//! introduction to CDCL, you are encouraged to look at the paper instead or to
9//! keep reading through this codebase and its comments.
10
11#![deny(missing_docs)]
12#![deny(unnameable_types)]
13
14mod conditional_requirement;
15pub mod conflict;
16pub(crate) mod internal;
17mod requirement;
18pub mod runtime;
19pub mod snapshot;
20mod solver;
21pub mod utils;
22
23use std::{
24    any::Any,
25    fmt::{Debug, Display},
26};
27
28pub use conditional_requirement::{Condition, ConditionalRequirement, LogicalOperator};
29pub use internal::arena::ArenaId;
30pub use internal::id::{
31    ConditionId, NameId, SolvableId, SolvableOrRootId, StringId, VersionSetId, VersionSetUnionId,
32};
33use itertools::Itertools;
34pub use requirement::Requirement;
35pub use solver::{Problem, Solver, SolverCache, UnsolvableOrCancelled};
36pub use utils::{Mapping, MappingIter};
37
38/// An object that is used by the solver to query certain properties of
39/// different internalized objects.
40pub trait Interner {
41    /// Returns an object that can be used to display the given solvable in a
42    /// user-friendly way.
43    ///
44    /// When formatting the solvable, it should it include both the name of
45    /// the package and any other identifying properties.
46    fn display_solvable(&self, solvable: SolvableId) -> impl Display + '_;
47
48    /// Returns an object that can be used to display the name of a solvable in
49    /// a user-friendly way.
50    fn display_solvable_name(&self, solvable: SolvableId) -> impl Display + '_ {
51        self.display_name(self.solvable_name(solvable))
52    }
53
54    /// Returns an object that can be used to display multiple solvables in a
55    /// user-friendly way. For example the conda provider should only display
56    /// the versions (not build strings etc.) and merges multiple solvables
57    /// into one line.
58    ///
59    /// When formatting the solvables, both the name of the package and any
60    /// other identifying properties should be displayed.
61    fn display_merged_solvables(&self, solvables: &[SolvableId]) -> impl Display + '_ {
62        if solvables.is_empty() {
63            return String::new();
64        }
65
66        let versions = solvables
67            .iter()
68            .map(|&id| self.display_solvable(id).to_string())
69            .sorted()
70            .unique()
71            .format(" | ");
72
73        let name = self.display_solvable_name(solvables[0]);
74        format!("{name} {versions}")
75    }
76
77    /// Returns an object that can be used to display the given name in a
78    /// user-friendly way.
79    fn display_name(&self, name: NameId) -> impl Display + '_;
80
81    /// Returns an object that can be used to display the given version set in a
82    /// user-friendly way.
83    ///
84    /// The name of the package should *not* be included in the display. Where
85    /// appropriate, this information is added.
86    fn display_version_set(&self, version_set: VersionSetId) -> impl Display + '_;
87
88    /// Displays the string with the given id.
89    fn display_string(&self, string_id: StringId) -> impl Display + '_;
90
91    /// Returns the name of the package that the specified version set is
92    /// associated with.
93    fn version_set_name(&self, version_set: VersionSetId) -> NameId;
94
95    /// Returns the name of the package for the given solvable.
96    fn solvable_name(&self, solvable: SolvableId) -> NameId;
97
98    /// Returns the version sets comprising the given union.
99    ///
100    /// The implementor must take care that the order in which the version sets
101    /// are returned is deterministic.
102    fn version_sets_in_union(
103        &self,
104        version_set_union: VersionSetUnionId,
105    ) -> impl Iterator<Item = VersionSetId>;
106
107    /// Resolves how a condition should be represented in the solver.
108    ///
109    /// Internally, the solver uses `ConditionId` to represent conditions. This
110    /// allows implementers to have a custom representation for conditions that
111    /// differ from the representation of the solver.
112    fn resolve_condition(&self, condition: ConditionId) -> Condition;
113}
114
115/// Defines implementation specific behavior for the solver and a way for the
116/// solver to access the packages that are available in the system.
117#[allow(async_fn_in_trait)]
118pub trait DependencyProvider: Sized + Interner {
119    /// Given a set of solvables, return the candidates that match the given
120    /// version set or if `inverse` is true, the candidates that do *not* match
121    /// the version set.
122    async fn filter_candidates(
123        &self,
124        candidates: &[SolvableId],
125        version_set: VersionSetId,
126        inverse: bool,
127    ) -> Vec<SolvableId>;
128
129    /// Obtains a list of solvables that should be considered when a package
130    /// with the given name is requested.
131    async fn get_candidates(&self, name: NameId) -> Option<Candidates>;
132
133    /// Sort the specified solvables based on which solvable to try first. The
134    /// solver will iteratively try to select the highest version. If a
135    /// conflict is found with the highest version the next version is
136    /// tried. This continues until a solution is found.
137    async fn sort_candidates(&self, solver: &SolverCache<Self>, solvables: &mut [SolvableId]);
138
139    /// Returns the dependencies for the specified solvable.
140    async fn get_dependencies(&self, solvable: SolvableId) -> Dependencies;
141
142    /// Whether the solver should stop the dependency resolution algorithm.
143    ///
144    /// This method gets called at the beginning of each unit propagation round
145    /// and before potentially blocking operations (like
146    /// [Self::get_dependencies] and [Self::get_candidates]). If it returns
147    /// `Some(...)`, the solver will stop and return
148    /// [UnsolvableOrCancelled::Cancelled].
149    fn should_cancel_with_value(&self) -> Option<Box<dyn Any>> {
150        None
151    }
152}
153
154/// A list of candidate solvables for a specific package. This is returned from
155/// [`DependencyProvider::get_candidates`].
156#[derive(Default, Clone, Debug)]
157pub struct Candidates {
158    /// A list of all solvables for the package.
159    pub candidates: Vec<SolvableId>,
160
161    /// Optionally the id of the solvable that is favored over other solvables.
162    /// The solver will first attempt to solve for the specified solvable
163    /// but will fall back to other candidates if no solution could be found
164    /// otherwise.
165    ///
166    /// The same behavior can be achieved by sorting this candidate to the top
167    /// using the [`DependencyProvider::sort_candidates`] function but using
168    /// this method provides better error messages to the user.
169    pub favored: Option<SolvableId>,
170
171    /// If specified this is the Id of the only solvable that can be selected.
172    /// Although it would also be possible to simply return a single
173    /// candidate using this field provides better error messages to the
174    /// user.
175    pub locked: Option<SolvableId>,
176
177    /// A hint to the solver that the dependencies of some of the solvables are
178    /// also directly available. This allows the solver to request the
179    /// dependencies of these solvables immediately. Having the dependency
180    /// information available might make the solver much faster because it
181    /// has more information available up-front which provides the solver with a
182    /// more complete picture of the entire problem space. However, it might
183    /// also be the case that the solver doesnt actually need this
184    /// information to form a solution. In general though, if the
185    /// dependencies can easily be provided one should provide them up-front.
186    pub hint_dependencies_available: HintDependenciesAvailable,
187
188    /// A list of solvables that are available but have been excluded from the
189    /// solver. For example, a package might be excluded from the solver
190    /// because it is not compatible with the runtime. The solver will not
191    /// consider these solvables when forming a solution but will use
192    /// them in the error message if no solution could be found.
193    pub excluded: Vec<(SolvableId, StringId)>,
194}
195
196/// Defines for which candidates dependencies are available without the
197/// [`DependencyProvider`] having to perform extra work, e.g. it's cheap to
198/// request them.
199#[derive(Default, Clone, Debug)]
200pub enum HintDependenciesAvailable {
201    /// None of the dependencies are available up-front. The dependency provide
202    /// will have to do work to find the dependencies.
203    #[default]
204    None,
205
206    /// All the dependencies are available up-front. Querying them is cheap.
207    All,
208
209    /// Only the dependencies for the specified solvables are available.
210    /// Querying the dependencies for these solvables is cheap. Querying
211    /// dependencies for other solvables is expensive.
212    Some(Vec<SolvableId>),
213}
214
215/// Holds information about the dependencies of a package.
216#[derive(Debug, Clone)]
217#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
218#[cfg_attr(feature = "serde", serde(untagged))]
219pub enum Dependencies {
220    /// The dependencies are known.
221    Known(KnownDependencies),
222    /// The dependencies are unknown, so the parent solvable should be excluded
223    /// from the solution.
224    ///
225    /// The string provides more information about why the dependencies are
226    /// unknown (e.g. an error message).
227    Unknown(StringId),
228}
229
230/// Holds information about the dependencies of a package when they are known.
231#[derive(Default, Clone, Debug)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
233pub struct KnownDependencies {
234    /// Defines which packages should be installed alongside the depending
235    /// package and the constraints applied to the package.
236    #[cfg_attr(
237        feature = "serde",
238        serde(default, skip_serializing_if = "Vec::is_empty")
239    )]
240    pub requirements: Vec<ConditionalRequirement>,
241
242    /// Defines additional constraints on packages that may or may not be part
243    /// of the solution. Different from `requirements`, packages in this set
244    /// are not necessarily included in the solution. Only when one or more
245    /// packages list the package in their `requirements` is the
246    /// package also added to the solution.
247    ///
248    /// This is often useful to use for optional dependencies.
249    #[cfg_attr(
250        feature = "serde",
251        serde(default, skip_serializing_if = "Vec::is_empty")
252    )]
253    pub constrains: Vec<VersionSetId>,
254}