1use std::fmt;
2
3use crate::core::{Dependency, PackageId, Registry, Summary};
4use crate::util::lev_distance::lev_distance;
5use crate::util::Config;
6use anyhow::Error;
7
8use super::context::Context;
9use super::types::{ConflictMap, ConflictReason};
10
11pub struct ResolveError {
13 cause: Error,
14 package_path: Vec<PackageId>,
15}
16
17impl ResolveError {
18 pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
19 Self {
20 cause: cause.into(),
21 package_path,
22 }
23 }
24
25 pub fn package_path(&self) -> &[PackageId] {
28 &self.package_path
29 }
30}
31
32impl std::error::Error for ResolveError {
33 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
34 self.cause.source()
35 }
36}
37
38impl fmt::Debug for ResolveError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 self.cause.fmt(f)
41 }
42}
43
44impl fmt::Display for ResolveError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 self.cause.fmt(f)
47 }
48}
49
50pub type ActivateResult<T> = Result<T, ActivateError>;
51
52#[derive(Debug)]
53pub enum ActivateError {
54 Fatal(anyhow::Error),
55 Conflict(PackageId, ConflictReason),
56}
57
58impl From<::anyhow::Error> for ActivateError {
59 fn from(t: ::anyhow::Error) -> Self {
60 ActivateError::Fatal(t)
61 }
62}
63
64impl From<(PackageId, ConflictReason)> for ActivateError {
65 fn from(t: (PackageId, ConflictReason)) -> Self {
66 ActivateError::Conflict(t.0, t.1)
67 }
68}
69
70pub(super) fn activation_error(
71 cx: &Context,
72 registry: &mut dyn Registry,
73 parent: &Summary,
74 dep: &Dependency,
75 conflicting_activations: &ConflictMap,
76 candidates: &[Summary],
77 config: Option<&Config>,
78) -> ResolveError {
79 let to_resolve_err = |err| {
80 ResolveError::new(
81 err,
82 cx.parents
83 .path_to_bottom(&parent.package_id())
84 .into_iter()
85 .cloned()
86 .collect(),
87 )
88 };
89
90 if !candidates.is_empty() {
91 let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
92 msg.push_str("\n ... required by ");
93 msg.push_str(&describe_path(
94 &cx.parents.path_to_bottom(&parent.package_id()),
95 ));
96
97 msg.push_str("\nversions that meet the requirements `");
98 msg.push_str(&dep.version_req().to_string());
99 msg.push_str("` are: ");
100 msg.push_str(
101 &candidates
102 .iter()
103 .map(|v| v.version())
104 .map(|v| v.to_string())
105 .collect::<Vec<_>>()
106 .join(", "),
107 );
108
109 let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
110 conflicting_activations.sort_unstable();
111 let (links_errors, mut other_errors): (Vec<_>, Vec<_>) = conflicting_activations
112 .drain(..)
113 .rev()
114 .partition(|&(_, r)| r.is_links());
115
116 for &(p, r) in links_errors.iter() {
117 if let ConflictReason::Links(ref link) = *r {
118 msg.push_str("\n\nthe package `");
119 msg.push_str(&*dep.package_name());
120 msg.push_str("` links to the native library `");
121 msg.push_str(link);
122 msg.push_str("`, but it conflicts with a previous package which links to `");
123 msg.push_str(link);
124 msg.push_str("` as well:\n");
125 }
126 msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
127 }
128
129 let (features_errors, mut other_errors): (Vec<_>, Vec<_>) = other_errors
130 .drain(..)
131 .partition(|&(_, r)| r.is_missing_features());
132
133 for &(p, r) in features_errors.iter() {
134 if let ConflictReason::MissingFeatures(ref features) = *r {
135 msg.push_str("\n\nthe package `");
136 msg.push_str(&*p.name());
137 msg.push_str("` depends on `");
138 msg.push_str(&*dep.package_name());
139 msg.push_str("`, with features: `");
140 msg.push_str(features);
141 msg.push_str("` but `");
142 msg.push_str(&*dep.package_name());
143 msg.push_str("` does not have these features.\n");
144 }
145 }
147
148 let (required_dependency_as_features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors
149 .drain(..)
150 .partition(|&(_, r)| r.is_required_dependency_as_features());
151
152 for &(p, r) in required_dependency_as_features_errors.iter() {
153 if let ConflictReason::RequiredDependencyAsFeatures(ref features) = *r {
154 msg.push_str("\n\nthe package `");
155 msg.push_str(&*p.name());
156 msg.push_str("` depends on `");
157 msg.push_str(&*dep.package_name());
158 msg.push_str("`, with features: `");
159 msg.push_str(features);
160 msg.push_str("` but `");
161 msg.push_str(&*dep.package_name());
162 msg.push_str("` does not have these features.\n");
163 msg.push_str(
164 " It has a required dependency with that name, \
165 but only optional dependencies can be used as features.\n",
166 );
167 }
168 }
170
171 if !other_errors.is_empty() {
172 msg.push_str(
173 "\n\nall possible versions conflict with \
174 previously selected packages.",
175 );
176 }
177
178 for &(p, _) in other_errors.iter() {
179 msg.push_str("\n\n previously selected ");
180 msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
181 }
182
183 msg.push_str("\n\nfailed to select a version for `");
184 msg.push_str(&*dep.package_name());
185 msg.push_str("` which could resolve this conflict");
186
187 return to_resolve_err(anyhow::format_err!("{}", msg));
188 }
189
190 let all_req = semver::VersionReq::parse("*").unwrap();
197 let mut new_dep = dep.clone();
198 new_dep.set_version_req(all_req);
199 let mut candidates = match registry.query_vec(&new_dep, false) {
200 Ok(candidates) => candidates,
201 Err(e) => return to_resolve_err(e),
202 };
203 candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));
204
205 let mut msg =
206 if !candidates.is_empty() {
207 let versions = {
208 let mut versions = candidates
209 .iter()
210 .take(3)
211 .map(|cand| cand.version().to_string())
212 .collect::<Vec<_>>();
213
214 if candidates.len() > 3 {
215 versions.push("...".into());
216 }
217
218 versions.join(", ")
219 };
220
221 let mut msg = format!(
222 "failed to select a version for the requirement `{} = \"{}\"`\n \
223 candidate versions found which didn't match: {}\n \
224 location searched: {}\n",
225 dep.package_name(),
226 dep.version_req(),
227 versions,
228 registry.describe_source(dep.source_id()),
229 );
230 msg.push_str("required by ");
231 msg.push_str(&describe_path(
232 &cx.parents.path_to_bottom(&parent.package_id()),
233 ));
234
235 if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') {
239 msg.push_str(
240 "\nconsider running `cargo update` to update \
241 a path dependency's locked version",
242 );
243 }
244
245 if registry.is_replaced(dep.source_id()) {
246 msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
247 }
248
249 msg
250 } else {
251 let mut candidates = Vec::new();
254 if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s), true) {
255 return to_resolve_err(e);
256 };
257 candidates.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
258 candidates.dedup_by(|a, b| a.name() == b.name());
259 let mut candidates: Vec<_> = candidates
260 .iter()
261 .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n))
262 .filter(|&(d, _)| d < 4)
263 .collect();
264 candidates.sort_by_key(|o| o.0);
265 let mut msg = format!(
266 "no matching package named `{}` found\n\
267 location searched: {}\n",
268 dep.package_name(),
269 dep.source_id()
270 );
271 if !candidates.is_empty() {
272 if dep.package_name() == candidates[0].1.name()
275 && candidates[0].1.package_id().version().is_prerelease()
276 {
277 msg.push_str("prerelease package needs to be specified explicitly\n");
278 msg.push_str(&format!(
279 "{name} = {{ version = \"{version}\" }}",
280 name = candidates[0].1.name(),
281 version = candidates[0].1.package_id().version()
282 ));
283 } else {
284 let mut names = candidates
285 .iter()
286 .take(3)
287 .map(|c| c.1.name().as_str())
288 .collect::<Vec<_>>();
289
290 if candidates.len() > 3 {
291 names.push("...");
292 }
293
294 msg.push_str("perhaps you meant: ");
295 msg.push_str(&names.iter().enumerate().fold(
296 String::default(),
297 |acc, (i, el)| match i {
298 0 => acc + el,
299 i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
300 _ => acc + ", " + el,
301 },
302 ));
303 }
304
305 msg.push_str("\n");
306 }
307 msg.push_str("required by ");
308 msg.push_str(&describe_path(
309 &cx.parents.path_to_bottom(&parent.package_id()),
310 ));
311
312 msg
313 };
314
315 if let Some(config) = config {
316 if config.offline() {
317 msg.push_str(
318 "\nAs a reminder, you're using offline mode (--offline) \
319 which can sometimes cause surprising resolution failures, \
320 if this error is too confusing you may wish to retry \
321 without the offline flag.",
322 );
323 }
324 }
325
326 to_resolve_err(anyhow::format_err!("{}", msg))
327}
328
329pub(super) fn describe_path(path: &[&PackageId]) -> String {
331 use std::fmt::Write;
332 let mut dep_path_desc = format!("package `{}`", path[0]);
333 for dep in path[1..].iter() {
334 write!(dep_path_desc, "\n ... which is depended on by `{}`", dep).unwrap();
335 }
336 dep_path_desc
337}