1 //! A `Source` for registry-based packages.
3 //! # What's a Registry?
5 //! Registries are central locations where packages can be uploaded to,
6 //! discovered, and searched for. The purpose of a registry is to have a
7 //! location that serves as permanent storage for versions of a crate over time.
9 //! Compared to git sources, a registry provides many packages as well as many
10 //! versions simultaneously. Git sources can also have commits deleted through
11 //! rebasings where registries cannot have their versions deleted.
13 //! # The Index of a Registry
15 //! One of the major difficulties with a registry is that hosting so many
16 //! packages may quickly run into performance problems when dealing with
17 //! dependency graphs. It's infeasible for cargo to download the entire contents
18 //! of the registry just to resolve one package's dependencies, for example. As
19 //! a result, cargo needs some efficient method of querying what packages are
20 //! available on a registry, what versions are available, and what the
21 //! dependencies for each version is.
23 //! One method of doing so would be having the registry expose an HTTP endpoint
24 //! which can be queried with a list of packages and a response of their
25 //! dependencies and versions is returned. This is somewhat inefficient however
26 //! as we may have to hit the endpoint many times and we may have already
27 //! queried for much of the data locally already (for other packages, for
28 //! example). This also involves inventing a transport format between the
29 //! registry and Cargo itself, so this route was not taken.
31 //! Instead, Cargo communicates with registries through a git repository
32 //! referred to as the Index. The Index of a registry is essentially an easily
33 //! query-able version of the registry's database for a list of versions of a
34 //! package as well as a list of dependencies for each version.
36 //! Using git to host this index provides a number of benefits:
38 //! * The entire index can be stored efficiently locally on disk. This means
39 //! that all queries of a registry can happen locally and don't need to touch
42 //! * Updates of the index are quite efficient. Using git buys incremental
43 //! updates, compressed transmission, etc for free. The index must be updated
44 //! each time we need fresh information from a registry, but this is one
45 //! update of a git repository that probably hasn't changed a whole lot so
46 //! it shouldn't be too expensive.
48 //! Additionally, each modification to the index is just appending a line at
49 //! the end of a file (the exact format is described later). This means that
50 //! the commits for an index are quite small and easily applied/compressible.
52 //! ## The format of the Index
54 //! The index is a store for the list of versions for all packages known, so its
55 //! format on disk is optimized slightly to ensure that `ls registry` doesn't
56 //! produce a list of all packages ever known. The index also wants to ensure
57 //! that there's not a million files which may actually end up hitting
58 //! filesystem limits at some point. To this end, a few decisions were made
59 //! about the format of the registry:
61 //! 1. Each crate will have one file corresponding to it. Each version for a
62 //! crate will just be a line in this file.
63 //! 2. There will be two tiers of directories for crate names, under which
64 //! crates corresponding to those tiers will be located.
66 //! As an example, this is an example hierarchy of an index:
87 //! The root of the index contains a `config.json` file with a few entries
88 //! corresponding to the registry (see [`RegistryConfig`] below).
90 //! Otherwise, there are three numbered directories (1, 2, 3) for crates with
91 //! names 1, 2, and 3 characters in length. The 1/2 directories simply have the
92 //! crate files underneath them, while the 3 directory is sharded by the first
93 //! letter of the crate name.
95 //! Otherwise the top-level directory contains many two-letter directory names,
96 //! each of which has many sub-folders with two letters. At the end of all these
97 //! are the actual crate files themselves.
99 //! The purpose of this layout is to hopefully cut down on `ls` sizes as well as
100 //! efficient lookup based on the crate name itself.
104 //! Each file in the index is the history of one crate over time. Each line in
105 //! the file corresponds to one version of a crate, stored in JSON format (see
106 //! the `RegistryPackage` structure below).
108 //! As new versions are published, new lines are appended to this file. The only
109 //! modifications to this file that should happen over time are yanks of a
110 //! particular version.
112 //! # Downloading Packages
114 //! The purpose of the Index was to provide an efficient method to resolve the
115 //! dependency graph for a package. So far we only required one network
116 //! interaction to update the registry's repository (yay!). After resolution has
117 //! been performed, however we need to download the contents of packages so we
118 //! can read the full manifest and build the source code.
120 //! To accomplish this, this source's `download` method will make an HTTP
121 //! request per-package requested to download tarballs into a local cache. These
122 //! tarballs will then be unpacked into a destination folder.
124 //! Note that because versions uploaded to the registry are frozen forever that
125 //! the HTTP download and unpacking can all be skipped if the version has
126 //! already been downloaded and unpacked. This caching allows us to only
127 //! download a package when absolutely necessary.
129 //! # Filesystem Hierarchy
131 //! Overall, the `$HOME/.cargo` looks like this when talking about the registry:
134 //! # A folder under which all registry metadata is hosted (similar to
135 //! # $HOME/.cargo/git)
136 //! $HOME/.cargo/registry/
138 //! # For each registry that cargo knows about (keyed by hostname + hash)
139 //! # there is a folder which is the checked out version of the index for
140 //! # the registry in this location. Note that this is done so cargo can
141 //! # support multiple registries simultaneously
143 //! registry1-<hash>/
144 //! registry2-<hash>/
147 //! # This folder is a cache for all downloaded tarballs from a registry.
148 //! # Once downloaded and verified, a tarball never changes.
150 //! registry1-<hash>/<pkg>-<version>.crate
153 //! # Location in which all tarballs are unpacked. Each tarball is known to
154 //! # be frozen after downloading, so transitively this folder is also
155 //! # frozen once its unpacked (it's never unpacked again)
157 //! registry1-<hash>/<pkg>-<version>/...
161 use std
::borrow
::Cow
;
162 use std
::collections
::BTreeMap
;
163 use std
::collections
::HashSet
;
164 use std
::fs
::{File, OpenOptions}
;
166 use std
::path
::{Path, PathBuf}
;
168 use anyhow
::Context
as _
;
169 use flate2
::read
::GzDecoder
;
171 use semver
::{Version, VersionReq}
;
172 use serde
::Deserialize
;
175 use crate::core
::dependency
::{DepKind, Dependency}
;
176 use crate::core
::source
::MaybePackage
;
177 use crate::core
::{Package, PackageId, Source, SourceId, Summary}
;
178 use crate::sources
::PathSource
;
179 use crate::util
::hex
;
180 use crate::util
::interning
::InternedString
;
181 use crate::util
::into_url
::IntoUrl
;
182 use crate::util
::{restricted_names, CargoResult, Config, Filesystem}
;
184 const PACKAGE_SOURCE_LOCK
: &str = ".cargo-ok";
185 pub const CRATES_IO_INDEX
: &str = "https://github.com/rust-lang/crates.io-index";
186 pub const CRATES_IO_REGISTRY
: &str = "crates-io";
187 const CRATE_TEMPLATE
: &str = "{crate}";
188 const VERSION_TEMPLATE
: &str = "{version}";
189 const PREFIX_TEMPLATE
: &str = "{prefix}";
190 const LOWER_PREFIX_TEMPLATE
: &str = "{lowerprefix}";
192 /// A "source" for a local (see `local::LocalRegistry`) or remote (see
193 /// `remote::RemoteRegistry`) registry.
195 /// This contains common functionality that is shared between the two registry
196 /// kinds, with the registry-specific logic implemented as part of the
197 /// [`RegistryData`] trait referenced via the `ops` field.
198 pub struct RegistrySource
<'cfg
> {
200 /// The path where crate files are extracted (`$CARGO_HOME/registry/src/$REG-HASH`).
201 src_path
: Filesystem
,
202 /// Local reference to [`Config`] for convenience.
203 config
: &'cfg Config
,
204 /// Whether or not the index has been updated.
206 /// This is used as an optimization to avoid updating if not needed, such
207 /// as `Cargo.lock` already exists and the index already contains the
208 /// locked entries. Or, to avoid updating multiple times.
210 /// Only remote registries really need to update. Local registries only
211 /// check that the index exists.
213 /// Abstraction for interfacing to the different registry kinds.
214 ops
: Box
<dyn RegistryData
+ 'cfg
>,
215 /// Interface for managing the on-disk index.
216 index
: index
::RegistryIndex
<'cfg
>,
217 /// A set of packages that should be allowed to be used, even if they are
220 /// This is populated from the entries in `Cargo.lock` to ensure that
221 /// `cargo update -p somepkg` won't unlock yanked entries in `Cargo.lock`.
222 /// Otherwise, the resolver would think that those entries no longer
223 /// exist, and it would trigger updates to unrelated packages.
224 yanked_whitelist
: HashSet
<PackageId
>,
227 /// The `config.json` file stored in the index.
228 #[derive(Deserialize)]
229 pub struct RegistryConfig
{
230 /// Download endpoint for all crates.
232 /// The string is a template which will generate the download URL for the
233 /// tarball of a specific version of a crate. The substrings `{crate}` and
234 /// `{version}` will be replaced with the crate's name and version
235 /// respectively. The substring `{prefix}` will be replaced with the
236 /// crate's prefix directory name, and the substring `{lowerprefix}` will
237 /// be replaced with the crate's prefix directory name converted to
240 /// For backwards compatibility, if the string does not contain any
241 /// markers (`{crate}`, `{version}`, `{prefix}`, or ``{lowerprefix}`), it
242 /// will be extended with `/{crate}/{version}/download` to
243 /// support registries like crates.io which were created before the
244 /// templating setup was created.
247 /// API endpoint for the registry. This is what's actually hit to perform
248 /// operations like yanks, owner modifications, publish new crates, etc.
249 /// If this is None, the registry does not support API commands.
250 pub api
: Option
<String
>,
253 /// The maximum version of the `v` field in the index this version of cargo
255 pub(crate) const INDEX_V_MAX
: u32 = 2;
257 /// A single line in the index representing a single version of a package.
258 #[derive(Deserialize)]
259 pub struct RegistryPackage
<'a
> {
260 name
: InternedString
,
263 deps
: Vec
<RegistryDependency
<'a
>>,
264 features
: BTreeMap
<InternedString
, Vec
<InternedString
>>,
265 /// This field contains features with new, extended syntax. Specifically,
266 /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`).
268 /// This is separated from `features` because versions older than 1.19
269 /// will fail to load due to not being able to parse the new syntax, even
270 /// with a `Cargo.lock` file.
271 features2
: Option
<BTreeMap
<InternedString
, Vec
<InternedString
>>>,
273 /// If `true`, Cargo will skip this version when resolving.
275 /// This was added in 2014. Everything in the crates.io index has this set
276 /// now, so this probably doesn't need to be an option anymore.
277 yanked
: Option
<bool
>,
278 /// Native library name this package links to.
280 /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
281 /// can be `None` if published before then.
282 links
: Option
<InternedString
>,
283 /// The schema version for this entry.
285 /// If this is None, it defaults to version 1. Entries with unknown
286 /// versions are ignored.
288 /// Version `2` format adds the `features2` field.
290 /// This provides a method to safely introduce changes to index entries
291 /// and allow older versions of cargo to ignore newer entries it doesn't
292 /// understand. This is honored as of 1.51, so unfortunately older
293 /// versions will ignore it, and potentially misinterpret version 2 and
296 /// The intent is that versions older than 1.51 will work with a
297 /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
298 /// update` or build a lock from scratch. In that case, cargo may
299 /// incorrectly select a new package that uses a new index format. A
300 /// workaround is to downgrade any packages that are incompatible with the
301 /// `--precise` flag of `cargo update`.
306 fn escaped_char_in_json() {
307 let _
: RegistryPackage
<'_
> = serde_json
::from_str(
308 r
#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#,
311 let _
: RegistryPackage
<'_
> = serde_json
::from_str(
312 r
#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"#
315 // Now we add escaped cher all the places they can go
316 // these are not valid, but it should error later than json parsing
317 let _
: RegistryPackage
<'_
> = serde_json
::from_str(
319 "name":"This name has a escaped cher in it \n\t\" ",
324 "features": [" \n\t\" "],
326 "default_features": true,
327 "target": " \n\t\" ",
329 "registry": " \n\t\" "
332 "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]},
333 "links":" \n\t\" "}"#,
338 /// A dependency as encoded in the index JSON.
339 #[derive(Deserialize)]
340 struct RegistryDependency
<'a
> {
341 name
: InternedString
,
344 features
: Vec
<InternedString
>,
346 default_features
: bool
,
347 target
: Option
<Cow
<'a
, str>>,
348 kind
: Option
<Cow
<'a
, str>>,
349 registry
: Option
<Cow
<'a
, str>>,
350 package
: Option
<InternedString
>,
351 public
: Option
<bool
>,
354 impl<'a
> RegistryDependency
<'a
> {
355 /// Converts an encoded dependency in the registry to a cargo dependency
356 pub fn into_dep(self, default: SourceId
) -> CargoResult
<Dependency
> {
357 let RegistryDependency
{
370 let id
= if let Some(registry
) = ®istry
{
371 SourceId
::for_registry(®istry
.into_url()?
)?
376 let mut dep
= Dependency
::parse_no_deprecated(package
.unwrap_or(name
), Some(&req
), id
)?
;
377 if package
.is_some() {
378 dep
.set_explicit_name_in_toml(name
);
380 let kind
= match kind
.as_deref().unwrap_or("") {
381 "dev" => DepKind
::Development
,
382 "build" => DepKind
::Build
,
383 _
=> DepKind
::Normal
,
386 let platform
= match target
{
387 Some(target
) => Some(target
.parse()?
),
391 // All dependencies are private by default
392 let public
= public
.unwrap_or(false);
394 // Unfortunately older versions of cargo and/or the registry ended up
395 // publishing lots of entries where the features array contained the
396 // empty feature, "", inside. This confuses the resolution process much
397 // later on and these features aren't actually valid, so filter them all
399 features
.retain(|s
| !s
.is_empty());
401 // In index, "registry" is null if it is from the same index.
402 // In Cargo.toml, "registry" is None if it is from the default
403 if !id
.is_default_registry() {
404 dep
.set_registry_id(id
);
407 dep
.set_optional(optional
)
408 .set_default_features(default_features
)
409 .set_features(features
)
410 .set_platform(platform
)
418 /// An abstract interface to handle both a local (see `local::LocalRegistry`)
419 /// and remote (see `remote::RemoteRegistry`) registry.
421 /// This allows [`RegistrySource`] to abstractly handle both registry kinds.
422 pub trait RegistryData
{
423 /// Performs initialization for the registry.
425 /// This should be safe to call multiple times, the implementation is
426 /// expected to not do any work if it is already prepared.
427 fn prepare(&self) -> CargoResult
<()>;
429 /// Returns the path to the index.
431 /// Note that different registries store the index in different formats
432 /// (remote=git, local=files).
433 fn index_path(&self) -> &Filesystem
;
435 /// Loads the JSON for a specific named package from the index.
437 /// * `root` is the root path to the index.
438 /// * `path` is the relative path to the package to load (like `ca/rg/cargo`).
439 /// * `data` is a callback that will receive the raw bytes of the index JSON file.
444 data
: &mut dyn FnMut(&[u8]) -> CargoResult
<()>,
445 ) -> CargoResult
<()>;
447 /// Loads the `config.json` file and returns it.
449 /// Local registries don't have a config, and return `None`.
450 fn config(&mut self) -> CargoResult
<Option
<RegistryConfig
>>;
452 /// Updates the index.
454 /// For a remote registry, this updates the index over the network. Local
455 /// registries only check that the index exists.
456 fn update_index(&mut self) -> CargoResult
<()>;
458 /// Prepare to start downloading a `.crate` file.
460 /// Despite the name, this doesn't actually download anything. If the
461 /// `.crate` is already downloaded, then it returns [`MaybeLock::Ready`].
462 /// If it hasn't been downloaded, then it returns [`MaybeLock::Download`]
463 /// which contains the URL to download. The [`crate::core::package::Downloads`]
464 /// system handles the actual download process. After downloading, it
465 /// calls [`Self::finish_download`] to save the downloaded file.
467 /// `checksum` is currently only used by local registries to verify the
468 /// file contents (because local registries never actually download
469 /// anything). Remote registries will validate the checksum in
470 /// `finish_download`. For already downloaded `.crate` files, it does not
471 /// validate the checksum, assuming the filesystem does not suffer from
472 /// corruption or manipulation.
473 fn download(&mut self, pkg
: PackageId
, checksum
: &str) -> CargoResult
<MaybeLock
>;
475 /// Finish a download by saving a `.crate` file to disk.
477 /// After [`crate::core::package::Downloads`] has finished a download,
478 /// it will call this to save the `.crate` file. This is only relevant
479 /// for remote registries. This should validate the checksum and save
480 /// the given data to the on-disk cache.
482 /// Returns a [`File`] handle to the `.crate` file, positioned at the start.
483 fn finish_download(&mut self, pkg
: PackageId
, checksum
: &str, data
: &[u8])
484 -> CargoResult
<File
>;
486 /// Returns whether or not the `.crate` file is already downloaded.
487 fn is_crate_downloaded(&self, _pkg
: PackageId
) -> bool
{
491 /// Validates that the global package cache lock is held.
493 /// Given the [`Filesystem`], this will make sure that the package cache
494 /// lock is held. If not, it will panic. See
495 /// [`Config::acquire_package_cache_lock`] for acquiring the global lock.
497 /// Returns the [`Path`] to the [`Filesystem`].
498 fn assert_index_locked
<'a
>(&self, path
: &'a Filesystem
) -> &'a Path
;
500 /// Returns the current "version" of the index.
502 /// For local registries, this returns `None` because there is no
503 /// versioning. For remote registries, this returns the SHA hash of the
504 /// git index on disk (or None if the index hasn't been downloaded yet).
506 /// This is used by index caching to check if the cache is out of date.
507 fn current_version(&self) -> Option
<InternedString
>;
510 /// The status of [`RegistryData::download`] which indicates if a `.crate`
511 /// file has already been downloaded, or if not then the URL to download.
513 /// The `.crate` file is already downloaded. [`File`] is a handle to the
514 /// opened `.crate` file on the filesystem.
516 /// The `.crate` file is not downloaded, here's the URL to download it from.
518 /// `descriptor` is just a text string to display to the user of what is
519 /// being downloaded.
520 Download { url: String, descriptor: String }
,
527 fn short_name(id
: SourceId
) -> String
{
528 let hash
= hex
::short_hash(&id
);
529 let ident
= id
.url().host_str().unwrap_or("").to_string();
530 format
!("{}-{}", ident
, hash
)
533 impl<'cfg
> RegistrySource
<'cfg
> {
536 yanked_whitelist
: &HashSet
<PackageId
>,
537 config
: &'cfg Config
,
538 ) -> RegistrySource
<'cfg
> {
539 let name
= short_name(source_id
);
540 let ops
= remote
::RemoteRegistry
::new(source_id
, config
, &name
);
541 RegistrySource
::new(source_id
, config
, &name
, Box
::new(ops
), yanked_whitelist
)
547 yanked_whitelist
: &HashSet
<PackageId
>,
548 config
: &'cfg Config
,
549 ) -> RegistrySource
<'cfg
> {
550 let name
= short_name(source_id
);
551 let ops
= local
::LocalRegistry
::new(path
, config
, &name
);
552 RegistrySource
::new(source_id
, config
, &name
, Box
::new(ops
), yanked_whitelist
)
557 config
: &'cfg Config
,
559 ops
: Box
<dyn RegistryData
+ 'cfg
>,
560 yanked_whitelist
: &HashSet
<PackageId
>,
561 ) -> RegistrySource
<'cfg
> {
563 src_path
: config
.registry_source_path().join(name
),
567 index
: index
::RegistryIndex
::new(source_id
, ops
.index_path(), config
),
568 yanked_whitelist
: yanked_whitelist
.clone(),
573 /// Decode the configuration stored within the registry.
575 /// This requires that the index has been at least checked out.
576 pub fn config(&mut self) -> CargoResult
<Option
<RegistryConfig
>> {
580 /// Unpacks a downloaded package into a location where it's ready to be
583 /// No action is taken if the source looks like it's already unpacked.
584 fn unpack_package(&self, pkg
: PackageId
, tarball
: &File
) -> CargoResult
<PathBuf
> {
585 // The `.cargo-ok` file is used to track if the source is already
587 let package_dir
= format
!("{}-{}", pkg
.name(), pkg
.version());
588 let dst
= self.src_path
.join(&package_dir
);
590 let path
= dst
.join(PACKAGE_SOURCE_LOCK
);
591 let path
= self.config
.assert_package_cache_locked(&path
);
592 let unpack_dir
= path
.parent().unwrap();
593 if let Ok(meta
) = path
.metadata() {
595 return Ok(unpack_dir
.to_path_buf());
598 let gz
= GzDecoder
::new(tarball
);
599 let mut tar
= Archive
::new(gz
);
600 let prefix
= unpack_dir
.file_name().unwrap();
601 let parent
= unpack_dir
.parent().unwrap();
602 for entry
in tar
.entries()?
{
603 let mut entry
= entry
.with_context(|| "failed to iterate over archive")?
;
604 let entry_path
= entry
606 .with_context(|| "failed to read entry path")?
609 // We're going to unpack this tarball into the global source
610 // directory, but we want to make sure that it doesn't accidentally
611 // (or maliciously) overwrite source code from other crates. Cargo
612 // itself should never generate a tarball that hits this error, and
613 // crates.io should also block uploads with these sorts of tarballs,
614 // but be extra sure by adding a check here as well.
615 if !entry_path
.starts_with(prefix
) {
617 "invalid tarball downloaded, contains \
618 a file at {:?} which isn't under {:?}",
624 let mut result
= entry
.unpack_in(parent
).map_err(anyhow
::Error
::from
);
625 if cfg
!(windows
) && restricted_names
::is_windows_reserved_path(&entry_path
) {
626 result
= result
.with_context(|| {
628 "`{}` appears to contain a reserved Windows path, \
629 it cannot be extracted on Windows",
635 .with_context(|| format
!("failed to unpack entry at `{}`", entry_path
.display()))?
;
638 // The lock file is created after unpacking so we overwrite a lock file
639 // which may have been extracted from the package.
640 let mut ok
= OpenOptions
::new()
645 .with_context(|| format
!("failed to open `{}`", path
.display()))?
;
647 // Write to the lock file to indicate that unpacking was successful.
650 Ok(unpack_dir
.to_path_buf())
653 fn do_update(&mut self) -> CargoResult
<()> {
654 self.ops
.update_index()?
;
655 let path
= self.ops
.index_path();
656 self.index
= index
::RegistryIndex
::new(self.source_id
, path
, self.config
);
661 fn get_pkg(&mut self, package
: PackageId
, path
: &File
) -> CargoResult
<Package
> {
663 .unpack_package(package
, path
)
664 .with_context(|| format
!("failed to unpack package `{}`", package
))?
;
665 let mut src
= PathSource
::new(&path
, self.source_id
, self.config
);
667 let mut pkg
= match src
.download(package
)?
{
668 MaybePackage
::Ready(pkg
) => pkg
,
669 MaybePackage
::Download { .. }
=> unreachable
!(),
672 // After we've loaded the package configure its summary's `checksum`
673 // field with the checksum we know for this `PackageId`.
674 let req
= VersionReq
::exact(package
.version());
675 let summary_with_cksum
= self
677 .summaries(package
.name(), &req
, &mut *self.ops
)?
678 .map(|s
| s
.summary
.clone())
680 .expect("summary not found");
681 if let Some(cksum
) = summary_with_cksum
.checksum() {
684 .set_checksum(cksum
.to_string());
691 impl<'cfg
> Source
for RegistrySource
<'cfg
> {
692 fn query(&mut self, dep
: &Dependency
, f
: &mut dyn FnMut(Summary
)) -> CargoResult
<()> {
693 // If this is a precise dependency, then it came from a lock file and in
694 // theory the registry is known to contain this version. If, however, we
695 // come back with no summaries, then our registry may need to be
696 // updated, so we fall back to performing a lazy update.
697 if dep
.source_id().precise().is_some() && !self.updated
{
698 debug
!("attempting query without update");
699 let mut called
= false;
701 .query_inner(dep
, &mut *self.ops
, &self.yanked_whitelist
, &mut |s
| {
710 debug
!("falling back to an update");
716 .query_inner(dep
, &mut *self.ops
, &self.yanked_whitelist
, &mut |s
| {
723 fn fuzzy_query(&mut self, dep
: &Dependency
, f
: &mut dyn FnMut(Summary
)) -> CargoResult
<()> {
725 .query_inner(dep
, &mut *self.ops
, &self.yanked_whitelist
, f
)
728 fn supports_checksums(&self) -> bool
{
732 fn requires_precise(&self) -> bool
{
736 fn source_id(&self) -> SourceId
{
740 fn update(&mut self) -> CargoResult
<()> {
741 // If we have an imprecise version then we don't know what we're going
742 // to look for, so we always attempt to perform an update here.
744 // If we have a precise version, then we'll update lazily during the
745 // querying phase. Note that precise in this case is only
746 // `Some("locked")` as other `Some` values indicate a `cargo update
747 // --precise` request
748 if self.source_id
.precise() != Some("locked") {
751 debug
!("skipping update due to locked registry");
756 fn download(&mut self, package
: PackageId
) -> CargoResult
<MaybePackage
> {
757 let hash
= self.index
.hash(package
, &mut *self.ops
)?
;
758 match self.ops
.download(package
, hash
)?
{
759 MaybeLock
::Ready(file
) => self.get_pkg(package
, &file
).map(MaybePackage
::Ready
),
760 MaybeLock
::Download { url, descriptor }
=> {
761 Ok(MaybePackage
::Download { url, descriptor }
)
766 fn finish_download(&mut self, package
: PackageId
, data
: Vec
<u8>) -> CargoResult
<Package
> {
767 let hash
= self.index
.hash(package
, &mut *self.ops
)?
;
768 let file
= self.ops
.finish_download(package
, hash
, &data
)?
;
769 self.get_pkg(package
, &file
)
772 fn fingerprint(&self, pkg
: &Package
) -> CargoResult
<String
> {
773 Ok(pkg
.package_id().version().to_string())
776 fn describe(&self) -> String
{
777 self.source_id
.display_index()
780 fn add_to_yanked_whitelist(&mut self, pkgs
: &[PackageId
]) {
781 self.yanked_whitelist
.extend(pkgs
);
784 fn is_yanked(&mut self, pkg
: PackageId
) -> CargoResult
<bool
> {
788 self.index
.is_yanked(pkg
, &mut *self.ops
)