]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
add test for wrong_case in registry
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
fc0e6426 1use std::collections::HashMap;
7192e29a 2
61a3c68b
AC
3use semver::VersionReq;
4use url::Url;
5
1e682848 6use core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary};
2d25fcac 7use core::PackageSet;
1e682848 8use util::{profile, Config};
e95044e3 9use util::errors::{CargoResult, CargoResultExt};
8214bb95 10use sources::config::SourceConfigMap;
f4d6d021 11
bacb6be3 12/// Source of information about a group of packages.
c7641c24
PK
13///
14/// See also `core::Source`.
f4d6d021 15pub trait Registry {
c7641c24 16 /// Attempt to find the packages that match a dependency request.
1e682848 17 fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
842c182e
AC
18
19 fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
20 let mut ret = Vec::new();
21 self.query(dep, &mut |s| ret.push(s))?;
22 Ok(ret)
23 }
8f6d4afb
CL
24}
25
816373d9
AC
26/// This structure represents a registry of known packages. It internally
27/// contains a number of `Box<Source>` instances which are used to load a
28/// `Package` from.
29///
30/// The resolution phase of Cargo uses this to drive knowledge about new
31/// packages as well as querying for lists of new packages. It is here that
bacb6be3 32/// sources are updated (e.g. network operations) and overrides are
816373d9
AC
33/// handled.
34///
35/// The general idea behind this registry is that it is centered around the
bacb6be3 36/// `SourceMap` structure, contained within which is a mapping of a `SourceId` to
816373d9
AC
37/// a `Source`. Each `Source` in the map has been updated (using network
38/// operations if necessary) and is ready to be queried for packages.
2fe0bf83 39pub struct PackageRegistry<'cfg> {
e3835f70 40 sources: SourceMap<'cfg>,
816373d9
AC
41
42 // A list of sources which are considered "overrides" which take precedent
43 // when querying for packages.
3b77b2c7 44 overrides: Vec<SourceId>,
6ab93117
AC
45
46 // Note that each SourceId does not take into account its `precise` field
47 // when hashing or testing for equality. When adding a new `SourceId`, we
48 // want to avoid duplicates in the `SourceMap` (to prevent re-updating the
49 // same git repo twice for example), but we also want to ensure that the
50 // loaded source is always updated.
51 //
52 // Sources with a `precise` field normally don't need to be updated because
53 // their contents are already on disk, but sources without a `precise` field
54 // almost always need to be updated. If we have a cached `Source` for a
55 // precise `SourceId`, then when we add a new `SourceId` that is not precise
56 // we want to ensure that the underlying source is updated.
57 //
58 // This is basically a long-winded way of saying that we want to know
59 // precisely what the keys of `sources` are, so this is a mapping of key to
60 // what exactly the key is.
61 source_ids: HashMap<SourceId, (SourceId, Kind)>,
62
e3835f70 63 locked: LockedMap,
8214bb95 64 source_config: SourceConfigMap<'cfg>,
eebddd37 65
61a3c68b 66 patches: HashMap<Url, Vec<Summary>>,
eebddd37
AC
67 patches_locked: bool,
68 patches_available: HashMap<Url, Vec<PackageId>>,
9b278af1
YKCL
69}
70
e3835f70
AC
71type LockedMap = HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>;
72
abe56727 73#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
74enum Kind {
75 Override,
76 Locked,
77 Normal,
78}
79
2fe0bf83 80impl<'cfg> PackageRegistry<'cfg> {
8214bb95 81 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
82655b46 82 let source_config = SourceConfigMap::new(config)?;
8214bb95 83 Ok(PackageRegistry {
e3835f70 84 sources: SourceMap::new(),
6ab93117 85 source_ids: HashMap::new(),
54d738b0 86 overrides: Vec::new(),
0247dc42 87 source_config,
816373d9 88 locked: HashMap::new(),
61a3c68b 89 patches: HashMap::new(),
eebddd37
AC
90 patches_locked: false,
91 patches_available: HashMap::new(),
8214bb95 92 })
98322afd
YKCL
93 }
94
f9945926 95 pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
e3835f70
AC
96 trace!("getting packages; sources={}", self.sources.len());
97 PackageSet::new(package_ids, self.sources)
5451f95d
CL
98 }
99
f0647ea2 100 fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {
edc6ebc5 101 match self.source_ids.get(namespace) {
6ab93117
AC
102 // We've previously loaded this source, and we've already locked it,
103 // so we're not allowed to change it even if `namespace` has a
104 // slightly different precise version listed.
f162aed2
AC
105 Some(&(_, Kind::Locked)) => {
106 debug!("load/locked {}", namespace);
1e682848 107 return Ok(());
f162aed2 108 }
6ab93117
AC
109
110 // If the previous source was not a precise source, then we can be
111 // sure that it's already been updated if we've already loaded it.
7a2facba 112 Some(&(ref previous, _)) if previous.precise().is_none() => {
f162aed2 113 debug!("load/precise {}", namespace);
1e682848 114 return Ok(());
6ab93117
AC
115 }
116
117 // If the previous source has the same precise version as we do,
118 // then we're done, otherwise we need to need to move forward
119 // updating this source.
120 Some(&(ref previous, _)) => {
7a2facba 121 if previous.precise() == namespace.precise() {
f162aed2 122 debug!("load/match {}", namespace);
1e682848 123 return Ok(());
6ab93117 124 }
f162aed2
AC
125 debug!("load/mismatch {}", namespace);
126 }
127 None => {
128 debug!("load/missing {}", namespace);
6ab93117 129 }
6ab93117 130 }
5451f95d 131
82655b46 132 self.load(namespace, kind)?;
9b278af1
YKCL
133 Ok(())
134 }
135
c94648ba
AC
136 pub fn add_sources(&mut self, ids: &[SourceId]) -> CargoResult<()> {
137 for id in ids.iter() {
82655b46 138 self.ensure_loaded(id, Kind::Locked)?;
1a35097a
AC
139 }
140 Ok(())
141 }
142
b02023ee
AK
143 pub fn add_preloaded(&mut self, source: Box<Source + 'cfg>) {
144 self.add_source(source, Kind::Locked);
bc60f64b
AC
145 }
146
68c6f2b0 147 fn add_source(&mut self, source: Box<Source + 'cfg>, kind: Kind) {
b02023ee 148 let id = source.source_id().clone();
e3835f70 149 self.sources.insert(source);
b02023ee 150 self.source_ids.insert(id.clone(), (id, kind));
bc60f64b
AC
151 }
152
b02023ee
AK
153 pub fn add_override(&mut self, source: Box<Source + 'cfg>) {
154 self.overrides.push(source.source_id().clone());
155 self.add_source(source, Kind::Override);
e465ace4
TCS
156 }
157
816373d9 158 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
155dee54
AC
159 trace!("register_lock: {}", id);
160 for dep in deps.iter() {
161 trace!("\t-> {}", dep);
162 }
1e682848
AC
163 let sub_map = self.locked
164 .entry(id.source_id().clone())
165 .or_insert_with(HashMap::new);
166 let sub_vec = sub_map
167 .entry(id.name().to_string())
168 .or_insert_with(Vec::new);
816373d9
AC
169 sub_vec.push((id, deps));
170 }
171
eebddd37
AC
172 /// Insert a `[patch]` section into this registry.
173 ///
174 /// This method will insert a `[patch]` section for the `url` specified,
175 /// with the given list of dependencies. The `url` specified is the URL of
176 /// the source to patch (for example this is `crates-io` in the manifest).
177 /// The `deps` is an array of all the entries in the `[patch]` section of
178 /// the manifest.
179 ///
180 /// Here the `deps` will be resolved to a precise version and stored
181 /// internally for future calls to `query` below. It's expected that `deps`
182 /// have had `lock_to` call already, if applicable. (e.g. if a lock file was
183 /// already present).
184 ///
185 /// Note that the patch list specified here *will not* be available to
186 /// `query` until `lock_patches` is called below, which should be called
187 /// once all patches have been added.
61a3c68b 188 pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> {
eebddd37
AC
189 // First up we need to actually resolve each `deps` specification to
190 // precisely one summary. We're not using the `query` method below as it
191 // internally uses maps we're building up as part of this method
192 // (`patches_available` and `patches). Instead we're going straight to
193 // the source to load information from it.
194 //
195 // Remember that each dependency listed in `[patch]` has to resolve to
196 // precisely one package, so that's why we're just creating a flat list
197 // of summaries which should be the same length as `deps` above.
1e682848
AC
198 let unlocked_summaries = deps.iter()
199 .map(|dep| {
200 debug!("registring a patch for `{}` with `{}`", url, dep.name());
201
202 // Go straight to the source for resolving `dep`. Load it as we
203 // normally would and then ask it directly for the list of summaries
204 // corresponding to this `dep`.
205 self.ensure_loaded(dep.source_id(), Kind::Normal)
206 .chain_err(|| {
207 format_err!(
208 "failed to load source for a dependency \
209 on `{}`",
210 dep.name()
211 )
212 })?;
213
214 let mut summaries = self.sources
215 .get_mut(dep.source_id())
216 .expect("loaded source not present")
217 .query_vec(dep)?
218 .into_iter();
219
220 let summary = match summaries.next() {
221 Some(summary) => summary,
222 None => bail!(
223 "patch for `{}` in `{}` did not resolve to any crates. If this is \
224 unexpected, you may wish to consult: \
225 https://github.com/rust-lang/cargo/issues/4678",
226 dep.name(),
227 url
228 ),
229 };
230 if summaries.next().is_some() {
231 bail!(
232 "patch for `{}` in `{}` resolved to more than one candidate",
233 dep.name(),
234 url
235 )
61a3c68b 236 }
1e682848
AC
237 if summary.package_id().source_id().url() == url {
238 bail!(
239 "patch for `{}` in `{}` points to the same source, but \
240 patches must point to different sources",
241 dep.name(),
242 url
243 );
244 }
245 Ok(summary)
246 })
247 .collect::<CargoResult<Vec<_>>>()
248 .chain_err(|| format_err!("failed to resolve patches for `{}`", url))?;
61a3c68b 249
eebddd37
AC
250 // Note that we do not use `lock` here to lock summaries! That step
251 // happens later once `lock_patches` is invoked. In the meantime though
252 // we want to fill in the `patches_available` map (later used in the
253 // `lock` method) and otherwise store the unlocked summaries in
254 // `patches` to get locked in a future call to `lock_patches`.
1e682848
AC
255 let ids = unlocked_summaries
256 .iter()
eebddd37
AC
257 .map(|s| s.package_id())
258 .cloned()
259 .collect();
260 self.patches_available.insert(url.clone(), ids);
261 self.patches.insert(url.clone(), unlocked_summaries);
61a3c68b
AC
262
263 Ok(())
264 }
265
eebddd37
AC
266 /// Lock all patch summaries added via `patch`, making them available to
267 /// resolution via `query`.
268 ///
269 /// This function will internally `lock` each summary added via `patch`
270 /// above now that the full set of `patch` packages are known. This'll allow
271 /// us to correctly resolve overridden dependencies between patches
272 /// hopefully!
273 pub fn lock_patches(&mut self) {
274 assert!(!self.patches_locked);
275 for summaries in self.patches.values_mut() {
276 for summary in summaries {
277 *summary = lock(&self.locked, &self.patches_available, summary.clone());
278 }
279 }
280 self.patches_locked = true;
281 }
282
61a3c68b
AC
283 pub fn patches(&self) -> &HashMap<Url, Vec<Summary>> {
284 &self.patches
285 }
286
6ab93117 287 fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 288 (|| {
1868998b 289 debug!("loading source {}", source_id);
82655b46 290 let source = self.source_config.load(source_id)?;
b02023ee 291 assert_eq!(source.source_id(), source_id);
8214bb95 292
38d14a59 293 if kind == Kind::Override {
3b77b2c7 294 self.overrides.push(source_id.clone());
f137281b 295 }
b02023ee 296 self.add_source(source, kind);
e465ace4 297
18e59304
AC
298 // Ensure the source has fetched all necessary remote data.
299 let _p = profile::start(format!("updating: {}", source_id));
e3835f70 300 self.sources.get_mut(source_id).unwrap().update()
1e682848
AC
301 })()
302 .chain_err(|| format_err!("Unable to update {}", source_id))?;
37cffbe0 303 Ok(())
9b278af1 304 }
46f90ba5 305
1e682848 306 fn query_overrides(&mut self, dep: &Dependency) -> CargoResult<Option<Summary>> {
3b77b2c7 307 for s in self.overrides.iter() {
e3835f70 308 let src = self.sources.get_mut(s).unwrap();
85f96a99 309 let dep = Dependency::new_override(&*dep.name(), s);
842c182e 310 let mut results = src.query_vec(&dep)?;
23591fe5 311 if !results.is_empty() {
1e682848 312 return Ok(Some(results.remove(0)));
fc0e6426 313 }
3b77b2c7 314 }
fc0e6426 315 Ok(None)
46f90ba5 316 }
816373d9 317
c0306a8a
AC
318 /// This function is used to transform a summary to another locked summary
319 /// if possible. This is where the concept of a lockfile comes into play.
320 ///
321 /// If a summary points at a package id which was previously locked, then we
322 /// override the summary's id itself, as well as all dependencies, to be
323 /// rewritten to the locked versions. This will transform the summary's
324 /// source to a precise source (listed in the locked version) as well as
325 /// transforming all of the dependencies from range requirements on
326 /// imprecise sources to exact requirements on precise sources.
327 ///
328 /// If a summary does not point at a package id which was previously locked,
329 /// or if any dependencies were added and don't have a previously listed
330 /// version, we still want to avoid updating as many dependencies as
331 /// possible to keep the graph stable. In this case we map all of the
332 /// summary's dependencies to be rewritten to a locked version wherever
333 /// possible. If we're unable to map a dependency though, we just pass it on
334 /// through.
335 pub fn lock(&self, summary: Summary) -> Summary {
eebddd37
AC
336 assert!(self.patches_locked);
337 lock(&self.locked, &self.patches_available, summary)
816373d9 338 }
fc0e6426 339
1e682848
AC
340 fn warn_bad_override(
341 &self,
342 override_summary: &Summary,
343 real_summary: &Summary,
344 ) -> CargoResult<()> {
772e1a17 345 let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>();
fc0e6426
AC
346
347 let boilerplate = "\
348This is currently allowed but is known to produce buggy behavior with spurious
349recompiles and changes to the crate graph. Path overrides unfortunately were
350never intended to support this feature, so for now this message is just a
351warning. In the future, however, this message will become a hard error.
352
353To change the dependency graph via an override it's recommended to use the
354`[replace]` feature of Cargo instead of the path override feature. This is
355documented online at the url below for more information.
356
357http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
358";
359
360 for dep in override_summary.dependencies() {
772e1a17 361 if let Some(i) = real_deps.iter().position(|d| dep == *d) {
fc0e6426 362 real_deps.remove(i);
1e682848 363 continue;
fc0e6426 364 }
1e682848
AC
365 let msg = format!(
366 "\
367 path override for crate `{}` has altered the original list of\n\
368 dependencies; the dependency on `{}` was either added or\n\
369 modified to not match the previously resolved version\n\n\
370 {}",
371 override_summary.package_id().name(),
372 dep.name(),
373 boilerplate
374 );
82655b46 375 self.source_config.config().shell().warn(&msg)?;
1e682848 376 return Ok(());
fc0e6426
AC
377 }
378
23591fe5 379 if let Some(id) = real_deps.get(0) {
1e682848
AC
380 let msg = format!(
381 "\
fc0e6426
AC
382 path override for crate `{}` has altered the original list of
383 dependencies; the dependency on `{}` was removed\n\n
1e682848
AC
384 {}",
385 override_summary.package_id().name(),
386 id.name(),
387 boilerplate
388 );
82655b46 389 self.source_config.config().shell().warn(&msg)?;
1e682848 390 return Ok(());
fc0e6426
AC
391 }
392
393 Ok(())
394 }
9b278af1
YKCL
395}
396
2fe0bf83 397impl<'cfg> Registry for PackageRegistry<'cfg> {
1e682848 398 fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
eebddd37 399 assert!(self.patches_locked);
e3835f70
AC
400 let (override_summary, n, to_warn) = {
401 // Look for an override and get ready to query the real source.
23591fe5 402 let override_summary = self.query_overrides(dep)?;
61a3c68b
AC
403
404 // Next up on our list of candidates is to check the `[patch]`
405 // section of the manifest. Here we look through all patches
406 // relevant to the source that `dep` points to, and then we match
407 // name/version. Note that we don't use `dep.matches(..)` because
408 // the patches, by definition, come from a different source.
409 // This means that `dep.matches(..)` will always return false, when
410 // what we really care about is the name/version match.
411 let mut patches = Vec::<Summary>::new();
412 if let Some(extra) = self.patches.get(dep.source_id().url()) {
1e682848
AC
413 patches.extend(
414 extra
415 .iter()
51d23560 416 .filter(|s| dep.matches_ignoring_source(s.package_id()))
1e682848
AC
417 .cloned(),
418 );
61a3c68b
AC
419 }
420
421 // A crucial feature of the `[patch]` feature is that we *don't*
422 // query the actual registry if we have a "locked" dependency. A
423 // locked dep basically just means a version constraint of `=a.b.c`,
424 // and because patches take priority over the actual source then if
425 // we have a candidate we're done.
426 if patches.len() == 1 && dep.is_locked() {
427 let patch = patches.remove(0);
428 match override_summary {
429 Some(summary) => (summary, 1, Some(patch)),
430 None => {
431 f(patch);
1e682848 432 return Ok(());
61a3c68b
AC
433 }
434 }
435 } else {
23591fe5 436 if !patches.is_empty() {
1e682848
AC
437 debug!(
438 "found {} patches with an unlocked dep on `{}` at {} \
439 with `{}`, \
440 looking at sources",
441 patches.len(),
442 dep.name(),
443 dep.source_id(),
444 dep.version_req()
445 );
e3835f70
AC
446 }
447
61a3c68b 448 // Ensure the requested source_id is loaded
1e682848
AC
449 self.ensure_loaded(dep.source_id(), Kind::Normal)
450 .chain_err(|| {
451 format_err!(
452 "failed to load source for a dependency \
453 on `{}`",
454 dep.name()
455 )
456 })?;
61a3c68b
AC
457
458 let source = self.sources.get_mut(dep.source_id());
459 match (override_summary, source) {
460 (Some(_), None) => bail!("override found but no real ones"),
461 (None, None) => return Ok(()),
462
463 // If we don't have an override then we just ship
464 // everything upstairs after locking the summary
465 (None, Some(source)) => {
466 for patch in patches.iter() {
467 f(patch.clone());
468 }
469
470 // Our sources shouldn't ever come back to us with two
471 // summaries that have the same version. We could,
472 // however, have an `[patch]` section which is in use
473 // to override a version in the registry. This means
474 // that if our `summary` in this loop has the same
475 // version as something in `patches` that we've
476 // already selected, then we skip this `summary`.
477 let locked = &self.locked;
eebddd37 478 let all_patches = &self.patches_available;
61a3c68b
AC
479 return source.query(dep, &mut |summary| {
480 for patch in patches.iter() {
481 let patch = patch.package_id().version();
482 if summary.package_id().version() == patch {
1e682848 483 return;
61a3c68b
AC
484 }
485 }
486 f(lock(locked, all_patches, summary))
1e682848 487 });
61a3c68b
AC
488 }
489
490 // If we have an override summary then we query the source
491 // to sanity check its results. We don't actually use any of
492 // the summaries it gives us though.
493 (Some(override_summary), Some(source)) => {
23591fe5 494 if !patches.is_empty() {
61a3c68b
AC
495 bail!("found patches and a path override")
496 }
497 let mut n = 0;
498 let mut to_warn = None;
499 source.query(dep, &mut |summary| {
500 n += 1;
501 to_warn = Some(summary);
502 })?;
503 (override_summary, n, to_warn)
504 }
e3835f70 505 }
86774bd8 506 }
816373d9
AC
507 };
508
842c182e
AC
509 if n > 1 {
510 bail!("found an override with a non-locked list");
511 } else if let Some(summary) = to_warn {
512 self.warn_bad_override(&override_summary, &summary)?;
513 }
514 f(self.lock(override_summary));
515 Ok(())
8f6d4afb 516 }
f4d6d021 517}
e465ace4 518
1e682848
AC
519fn lock(locked: &LockedMap, patches: &HashMap<Url, Vec<PackageId>>, summary: Summary) -> Summary {
520 let pair = locked
521 .get(summary.source_id())
522 .and_then(|map| map.get(&*summary.name()))
523 .and_then(|vec| vec.iter().find(|&&(ref id, _)| id == summary.package_id()));
e3835f70
AC
524
525 trace!("locking summary of {}", summary.package_id());
526
527 // Lock the summary's id if possible
528 let summary = match pair {
529 Some(&(ref precise, _)) => summary.override_id(precise.clone()),
530 None => summary,
531 };
61a3c68b 532 summary.map_dependencies(|dep| {
1e682848 533 trace!("\t{}/{}/{}", dep.name(), dep.version_req(), dep.source_id());
e3835f70
AC
534
535 // If we've got a known set of overrides for this summary, then
536 // one of a few cases can arise:
537 //
538 // 1. We have a lock entry for this dependency from the same
539 // source as it's listed as coming from. In this case we make
540 // sure to lock to precisely the given package id.
541 //
542 // 2. We have a lock entry for this dependency, but it's from a
543 // different source than what's listed, or the version
544 // requirement has changed. In this case we must discard the
545 // locked version because the dependency needs to be
546 // re-resolved.
547 //
548 // 3. We don't have a lock entry for this dependency, in which
549 // case it was likely an optional dependency which wasn't
550 // included previously so we just pass it through anyway.
551 //
552 // Cases 1/2 are handled by `matches_id` and case 3 is handled by
553 // falling through to the logic below.
554 if let Some(&(_, ref locked_deps)) = pair {
555 let locked = locked_deps.iter().find(|id| dep.matches_id(id));
556 if let Some(locked) = locked {
557 trace!("\tfirst hit on {}", locked);
61a3c68b 558 let mut dep = dep.clone();
e3835f70 559 dep.lock_to(locked);
1e682848 560 return dep;
e3835f70
AC
561 }
562 }
563
564 // If this dependency did not have a locked version, then we query
565 // all known locked packages to see if they match this dependency.
566 // If anything does then we lock it to that and move on.
1e682848
AC
567 let v = locked
568 .get(dep.source_id())
569 .and_then(|map| map.get(&*dep.name()))
570 .and_then(|vec| vec.iter().find(|&&(ref id, _)| dep.matches_id(id)));
61a3c68b
AC
571 if let Some(&(ref id, _)) = v {
572 trace!("\tsecond hit on {}", id);
573 let mut dep = dep.clone();
574 dep.lock_to(id);
1e682848 575 return dep;
61a3c68b
AC
576 }
577
578 // Finally we check to see if any registered patches correspond to
579 // this dependency.
580 let v = patches.get(dep.source_id().url()).map(|vec| {
581 let dep2 = dep.clone();
eebddd37 582 let mut iter = vec.iter().filter(move |p| {
1e682848 583 dep2.name() == p.name() && dep2.version_req().matches(p.version())
61a3c68b
AC
584 });
585 (iter.next(), iter)
586 });
eebddd37 587 if let Some((Some(patch_id), mut remaining)) = v {
61a3c68b 588 assert!(remaining.next().is_none());
eebddd37 589 let patch_source = patch_id.source_id();
1e682848
AC
590 let patch_locked = locked
591 .get(patch_source)
592 .and_then(|m| m.get(&*patch_id.name()))
593 .map(|list| list.iter().any(|&(ref id, _)| id == patch_id))
594 .unwrap_or(false);
61a3c68b
AC
595
596 if patch_locked {
eebddd37
AC
597 trace!("\tthird hit on {}", patch_id);
598 let req = VersionReq::exact(patch_id.version());
61a3c68b
AC
599 let mut dep = dep.clone();
600 dep.set_version_req(req);
1e682848 601 return dep;
e3835f70 602 }
e3835f70 603 }
61a3c68b
AC
604
605 trace!("\tnope, unlocked");
23591fe5 606 dep
e3835f70
AC
607 })
608}