]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
Implement source redirection
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
2d25fcac 1use std::collections::{HashSet, HashMap};
7192e29a 2
5451f95d 3use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package};
2d25fcac 4use core::PackageSet;
f9945926 5use util::{CargoResult, ChainError, Config, human, profile};
8214bb95 6use sources::config::SourceConfigMap;
f4d6d021 7
bacb6be3 8/// Source of information about a group of packages.
c7641c24
PK
9///
10/// See also `core::Source`.
f4d6d021 11pub trait Registry {
c7641c24 12 /// Attempt to find the packages that match a dependency request.
9b278af1 13 fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>>;
8f6d4afb
CL
14}
15
16impl Registry for Vec<Summary> {
46f90ba5 17 fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
3b77b2c7 18 Ok(self.iter().filter(|summary| dep.matches(*summary))
ebceb9bd 19 .cloned().collect())
9b278af1
YKCL
20 }
21}
22
348c7389
AC
23impl Registry for Vec<Package> {
24 fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
25 Ok(self.iter().filter(|pkg| dep.matches(pkg.summary()))
26 .map(|pkg| pkg.summary().clone()).collect())
27 }
28}
29
7ff9cbeb
AC
30impl<'a, T: ?Sized + Registry + 'a> Registry for Box<T> {
31 fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
32 (**self).query(name)
33 }
34}
35
816373d9
AC
36/// This structure represents a registry of known packages. It internally
37/// contains a number of `Box<Source>` instances which are used to load a
38/// `Package` from.
39///
40/// The resolution phase of Cargo uses this to drive knowledge about new
41/// packages as well as querying for lists of new packages. It is here that
bacb6be3 42/// sources are updated (e.g. network operations) and overrides are
816373d9
AC
43/// handled.
44///
45/// The general idea behind this registry is that it is centered around the
bacb6be3 46/// `SourceMap` structure, contained within which is a mapping of a `SourceId` to
816373d9
AC
47/// a `Source`. Each `Source` in the map has been updated (using network
48/// operations if necessary) and is ready to be queried for packages.
2fe0bf83
AC
49pub struct PackageRegistry<'cfg> {
50 sources: SourceMap<'cfg>,
816373d9
AC
51
52 // A list of sources which are considered "overrides" which take precedent
53 // when querying for packages.
3b77b2c7 54 overrides: Vec<SourceId>,
6ab93117
AC
55
56 // Note that each SourceId does not take into account its `precise` field
57 // when hashing or testing for equality. When adding a new `SourceId`, we
58 // want to avoid duplicates in the `SourceMap` (to prevent re-updating the
59 // same git repo twice for example), but we also want to ensure that the
60 // loaded source is always updated.
61 //
62 // Sources with a `precise` field normally don't need to be updated because
63 // their contents are already on disk, but sources without a `precise` field
64 // almost always need to be updated. If we have a cached `Source` for a
65 // precise `SourceId`, then when we add a new `SourceId` that is not precise
66 // we want to ensure that the underlying source is updated.
67 //
68 // This is basically a long-winded way of saying that we want to know
69 // precisely what the keys of `sources` are, so this is a mapping of key to
70 // what exactly the key is.
71 source_ids: HashMap<SourceId, (SourceId, Kind)>,
72
816373d9 73 locked: HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>,
8214bb95 74 source_config: SourceConfigMap<'cfg>,
9b278af1
YKCL
75}
76
abe56727 77#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
78enum Kind {
79 Override,
80 Locked,
81 Normal,
82}
83
2fe0bf83 84impl<'cfg> PackageRegistry<'cfg> {
8214bb95
AC
85 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
86 let source_config = try!(SourceConfigMap::new(config));
87 Ok(PackageRegistry {
5451f95d 88 sources: SourceMap::new(),
6ab93117 89 source_ids: HashMap::new(),
54d738b0 90 overrides: Vec::new(),
8214bb95 91 source_config: source_config,
816373d9 92 locked: HashMap::new(),
8214bb95 93 })
98322afd
YKCL
94 }
95
f9945926 96 pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
98854f6f 97 trace!("getting packages; sources={}", self.sources.len());
f9945926 98 PackageSet::new(package_ids, self.sources)
5451f95d
CL
99 }
100
f0647ea2 101 fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {
edc6ebc5 102 match self.source_ids.get(namespace) {
6ab93117
AC
103 // We've previously loaded this source, and we've already locked it,
104 // so we're not allowed to change it even if `namespace` has a
105 // slightly different precise version listed.
f162aed2
AC
106 Some(&(_, Kind::Locked)) => {
107 debug!("load/locked {}", namespace);
108 return Ok(())
109 }
6ab93117
AC
110
111 // If the previous source was not a precise source, then we can be
112 // sure that it's already been updated if we've already loaded it.
7a2facba 113 Some(&(ref previous, _)) if previous.precise().is_none() => {
f162aed2 114 debug!("load/precise {}", namespace);
6ab93117
AC
115 return Ok(())
116 }
117
118 // If the previous source has the same precise version as we do,
119 // then we're done, otherwise we need to need to move forward
120 // updating this source.
121 Some(&(ref previous, _)) => {
7a2facba 122 if previous.precise() == namespace.precise() {
f162aed2 123 debug!("load/match {}", namespace);
6ab93117
AC
124 return Ok(())
125 }
f162aed2
AC
126 debug!("load/mismatch {}", namespace);
127 }
128 None => {
129 debug!("load/missing {}", namespace);
6ab93117 130 }
6ab93117 131 }
5451f95d 132
f0647ea2 133 try!(self.load(namespace, kind));
9b278af1
YKCL
134 Ok(())
135 }
136
c94648ba
AC
137 pub fn add_sources(&mut self, ids: &[SourceId]) -> CargoResult<()> {
138 for id in ids.iter() {
f0647ea2 139 try!(self.ensure_loaded(id, Kind::Locked));
1a35097a
AC
140 }
141 Ok(())
142 }
143
bc60f64b
AC
144 pub fn add_preloaded(&mut self, id: &SourceId, source: Box<Source + 'cfg>) {
145 self.add_source(id, source, Kind::Locked);
146 }
147
148 fn add_source(&mut self, id: &SourceId, source: Box<Source + 'cfg>,
149 kind: Kind) {
150 self.sources.insert(id, source);
151 self.source_ids.insert(id.clone(), (id.clone(), kind));
152 }
153
a71e5743
AC
154 pub fn add_override(&mut self, id: &SourceId, source: Box<Source + 'cfg>) {
155 self.add_source(id, source, Kind::Override);
156 self.overrides.push(id.clone());
e465ace4
TCS
157 }
158
816373d9 159 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
6e6dec8a
AC
160 let sub_map = self.locked.entry(id.source_id().clone())
161 .or_insert(HashMap::new());
162 let sub_vec = sub_map.entry(id.name().to_string())
163 .or_insert(Vec::new());
816373d9
AC
164 sub_vec.push((id, deps));
165 }
166
6ab93117 167 fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 168 (|| {
8214bb95
AC
169 let source = try!(self.source_config.load(source_id));
170
38d14a59 171 if kind == Kind::Override {
3b77b2c7 172 self.overrides.push(source_id.clone());
f137281b 173 }
bc60f64b 174 self.add_source(source_id, source, kind);
e465ace4 175
18e59304
AC
176 // Ensure the source has fetched all necessary remote data.
177 let _p = profile::start(format!("updating: {}", source_id));
178 self.sources.get_mut(source_id).unwrap().update()
e465ace4 179 }).chain_error(|| human(format!("Unable to update {}", source_id)))
9b278af1 180 }
46f90ba5 181
3b77b2c7
AC
182 fn query_overrides(&mut self, dep: &Dependency)
183 -> CargoResult<Vec<Summary>> {
7192e29a 184 let mut seen = HashSet::new();
3b77b2c7
AC
185 let mut ret = Vec::new();
186 for s in self.overrides.iter() {
187 let src = self.sources.get_mut(s).unwrap();
7a2facba 188 let dep = Dependency::new_override(dep.name(), s);
03e3dba1 189 ret.extend(try!(src.query(&dep)).into_iter().filter(|s| {
7a2facba 190 seen.insert(s.name().to_string())
7192e29a 191 }));
3b77b2c7
AC
192 }
193 Ok(ret)
46f90ba5 194 }
816373d9
AC
195
196 // This function is used to transform a summary to another locked summary if
b186d2f5 197 // possible. This is where the concept of a lockfile comes into play.
816373d9
AC
198 //
199 // If a summary points at a package id which was previously locked, then we
bacb6be3 200 // override the summary's id itself, as well as all dependencies, to be
816373d9
AC
201 // rewritten to the locked versions. This will transform the summary's
202 // source to a precise source (listed in the locked version) as well as
203 // transforming all of the dependencies from range requirements on imprecise
204 // sources to exact requirements on precise sources.
205 //
206 // If a summary does not point at a package id which was previously locked,
207 // we still want to avoid updating as many dependencies as possible to keep
208 // the graph stable. In this case we map all of the summary's dependencies
209 // to be rewritten to a locked version wherever possible. If we're unable to
210 // map a dependency though, we just pass it on through.
211 fn lock(&self, summary: Summary) -> Summary {
7a2facba
AC
212 let pair = self.locked.get(summary.source_id()).and_then(|map| {
213 map.get(summary.name())
816373d9 214 }).and_then(|vec| {
7a2facba 215 vec.iter().find(|&&(ref id, _)| id == summary.package_id())
816373d9
AC
216 });
217
218 // Lock the summary's id if possible
219 let summary = match pair {
220 Some(&(ref precise, _)) => summary.override_id(precise.clone()),
221 None => summary,
222 };
223 summary.map_dependencies(|dep| {
224 match pair {
2ba5c5ec
AC
225 // If we've got a known set of overrides for this summary, then
226 // one of a few cases can arise:
227 //
228 // 1. We have a lock entry for this dependency from the same
bacb6be3 229 // source as it's listed as coming from. In this case we make
2ba5c5ec
AC
230 // sure to lock to precisely the given package id.
231 //
232 // 2. We have a lock entry for this dependency, but it's from a
493d3108
AC
233 // different source than what's listed, or the version
234 // requirement has changed. In this case we must discard the
235 // locked version because the dependency needs to be
236 // re-resolved.
2ba5c5ec
AC
237 //
238 // 3. We don't have a lock entry for this dependency, in which
239 // case it was likely an optional dependency which wasn't
240 // included previously so we just pass it through anyway.
816373d9 241 Some(&(_, ref deps)) => {
7a2facba 242 match deps.iter().find(|d| d.name() == dep.name()) {
2ba5c5ec 243 Some(lock) => {
493d3108 244 if dep.matches_id(lock) {
2ba5c5ec
AC
245 dep.lock_to(lock)
246 } else {
247 dep
248 }
249 }
816373d9
AC
250 None => dep,
251 }
252 }
253
254 // If this summary did not have a locked version, then we query
255 // all known locked packages to see if they match this
256 // dependency. If anything does then we lock it to that and move
257 // on.
258 None => {
7a2facba
AC
259 let v = self.locked.get(dep.source_id()).and_then(|map| {
260 map.get(dep.name())
816373d9
AC
261 }).and_then(|vec| {
262 vec.iter().find(|&&(ref id, _)| dep.matches_id(id))
263 });
264 match v {
265 Some(&(ref id, _)) => dep.lock_to(id),
266 None => dep
267 }
268 }
269 }
270 })
271 }
9b278af1
YKCL
272}
273
2fe0bf83 274impl<'cfg> Registry for PackageRegistry<'cfg> {
9b278af1 275 fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
54d738b0 276 let overrides = try!(self.query_overrides(&dep));
9b278af1 277
8628dbeb 278 let ret = if overrides.is_empty() {
e465ace4 279 // Ensure the requested source_id is loaded
a504f480
AC
280 try!(self.ensure_loaded(dep.source_id(), Kind::Normal).chain_error(|| {
281 human(format!("failed to load source for a dependency \
282 on `{}`", dep.name()))
283 }));
54d738b0
AC
284
285 match self.sources.get_mut(dep.source_id()) {
286 Some(src) => try!(src.query(&dep)),
287 None => Vec::new(),
3b77b2c7 288 }
9b278af1 289 } else {
816373d9
AC
290 overrides
291 };
292
293 // post-process all returned summaries to ensure that we lock all
294 // relevant summaries to the right versions and sources
295 Ok(ret.into_iter().map(|summary| self.lock(summary)).collect())
8f6d4afb 296 }
f4d6d021 297}
e465ace4
TCS
298
299#[cfg(test)]
300pub mod test {
301 use core::{Summary, Registry, Dependency};
302 use util::{CargoResult};
303
304 pub struct RegistryBuilder {
305 summaries: Vec<Summary>,
306 overrides: Vec<Summary>
307 }
308
309 impl RegistryBuilder {
310 pub fn new() -> RegistryBuilder {
ab1cb51f 311 RegistryBuilder { summaries: vec![], overrides: vec![] }
e465ace4
TCS
312 }
313
314 pub fn summary(mut self, summary: Summary) -> RegistryBuilder {
315 self.summaries.push(summary);
316 self
317 }
318
319 pub fn summaries(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 320 self.summaries.extend(summaries.into_iter());
e465ace4
TCS
321 self
322 }
323
c12a64f9 324 pub fn add_override(mut self, summary: Summary) -> RegistryBuilder {
e465ace4
TCS
325 self.overrides.push(summary);
326 self
327 }
328
329 pub fn overrides(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 330 self.overrides.extend(summaries.into_iter());
e465ace4
TCS
331 self
332 }
333
334 fn query_overrides(&self, dep: &Dependency) -> Vec<Summary> {
335 self.overrides.iter()
7a2facba 336 .filter(|s| s.name() == dep.name())
e465ace4
TCS
337 .map(|s| s.clone())
338 .collect()
339 }
340 }
341
342 impl Registry for RegistryBuilder {
343 fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
55321111 344 debug!("querying; dep={:?}", dep);
e465ace4
TCS
345
346 let overrides = self.query_overrides(dep);
347
348 if overrides.is_empty() {
349 self.summaries.query(dep)
350 } else {
351 Ok(overrides)
352 }
353 }
354 }
355}