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