#
cargo/sources/
source.rs

    1//! [`Source`] trait for sources of Cargo packages.
    2
    3use std::collections::hash_map::HashMap;
    4use std::fmt;
    5use std::task::Poll;
    6
    7use crate::core::package::PackageSet;
    8use crate::core::SourceId;
    9use crate::core::{Dependency, Package, PackageId};
    10use crate::sources::IndexSummary;
    11use crate::util::{CargoResult, GlobalContext};
    12
    13/// An abstraction of different sources of Cargo packages.
    14///
    15/// The [`Source`] trait generalizes the API to interact with these providers.
    16/// For example,
    17///
    18/// * [`Source::query`] is for querying package metadata on a given
    19///   [`Dependency`] requested by a Cargo manifest.
    20/// * [`Source::download`] is for fetching the full package information on
    21///   given names and versions.
    22/// * [`Source::source_id`] is for defining an unique identifier of a source to
    23///   distinguish one source from another, keeping Cargo safe from [dependency
    24///   confusion attack].
    25///
    26/// Normally, developers don't need to implement their own [`Source`]s. Cargo
    27/// provides several kinds of sources implementations that should cover almost
    28/// all use cases. See [`crate::sources`] for implementations provided by Cargo.
    29///
    30/// [dependency confusion attack]: https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
    31pub trait Source {
    32    /// Returns the [`SourceId`] corresponding to this source.
    33    fn source_id(&self) -> SourceId;
    34
    35    /// Returns the replaced [`SourceId`] corresponding to this source.
    36    fn replaced_source_id(&self) -> SourceId {
    37        self.source_id()
    38    }
    39
    40    /// Returns whether or not this source will return [`IndexSummary`] items with
    41    /// checksums listed.
    42    fn supports_checksums(&self) -> bool;
    43
    44    /// Returns whether or not this source will return [`IndexSummary`] items with
    45    /// the `precise` field in the [`SourceId`] listed.
    46    fn requires_precise(&self) -> bool;
    47
    48    /// Attempts to find the packages that match a dependency request.
    49    ///
    50    /// Usually you should call [`Source::block_until_ready`] somewhere and
    51    /// wait until package information become available. Otherwise any query
    52    /// may return a [`Poll::Pending`].
    53    ///
    54    /// The `f` argument is expected to get called when any [`IndexSummary`] becomes available.
    55    fn query(
    56        &mut self,
    57        dep: &Dependency,
    58        kind: QueryKind,
    59        f: &mut dyn FnMut(IndexSummary),
    60    ) -> Poll<CargoResult<()>>;
    61
    62    /// Gathers the result from [`Source::query`] as a list of [`IndexSummary`] items
    63    /// when they become available.
    64    fn query_vec(
    65        &mut self,
    66        dep: &Dependency,
    67        kind: QueryKind,
    68    ) -> Poll<CargoResult<Vec<IndexSummary>>> {
    69        let mut ret = Vec::new();
    70        self.query(dep, kind, &mut |s| ret.push(s)).map_ok(|_| ret)
    71    }
    72
    73    /// Ensure that the source is fully up-to-date for the current session on the next query.
    74    fn invalidate_cache(&mut self);
    75
    76    /// If quiet, the source should not display any progress or status messages.
    77    fn set_quiet(&mut self, quiet: bool);
    78
    79    /// Starts the process to fetch a [`Package`] for the given [`PackageId`].
    80    ///
    81    /// If the source already has the package available on disk, then it
    82    /// should return immediately with [`MaybePackage::Ready`] with the
    83    /// [`Package`]. Otherwise it should return a [`MaybePackage::Download`]
    84    /// to indicate the URL to download the package (this is for remote
    85    /// registry sources only).
    86    ///
    87    /// In the case where [`MaybePackage::Download`] is returned, then the
    88    /// package downloader will call [`Source::finish_download`] after the
    89    /// download has finished.
    90    fn download(&mut self, package: PackageId) -> CargoResult<MaybePackage>;
    91
    92    /// Convenience method used to **immediately** fetch a [`Package`] for the
    93    /// given [`PackageId`].
    94    ///
    95    /// This may trigger a download if necessary. This should only be used
    96    /// when a single package is needed (as in the case for `cargo install`).
    97    /// Otherwise downloads should be batched together via [`PackageSet`].
    98    fn download_now(
    99        self: Box<Self>,
    100        package: PackageId,
    101        gctx: &GlobalContext,
    102    ) -> CargoResult<Package>
    103    where
    104        Self: std::marker::Sized,
    105    {
    106        let mut sources = SourceMap::new();
    107        sources.insert(self);
    108        let pkg_set = PackageSet::new(&[package], sources, gctx)?;
    109        let pkg = pkg_set.get_one(package)?;
    110        Ok(Package::clone(pkg))
    111    }
    112
    113    /// Gives the source the downloaded `.crate` file.
    114    ///
    115    /// When a source has returned [`MaybePackage::Download`] in the
    116    /// [`Source::download`] method, then this function will be called with
    117    /// the results of the download of the given URL. The source is
    118    /// responsible for saving to disk, and returning the appropriate
    119    /// [`Package`].
    120    fn finish_download(&mut self, pkg_id: PackageId, contents: Vec<u8>) -> CargoResult<Package>;
    121
    122    /// Generates a unique string which represents the fingerprint of the
    123    /// current state of the source.
    124    ///
    125    /// This fingerprint is used to determine the "freshness" of the source
    126    /// later on. It must be guaranteed that the fingerprint of a source is
    127    /// constant if and only if the output product will remain constant.
    128    ///
    129    /// The `pkg` argument is the package which this fingerprint should only be
    130    /// interested in for when this source may contain multiple packages.
    131    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
    132
    133    /// If this source supports it, verifies the source of the package
    134    /// specified.
    135    ///
    136    /// Note that the source may also have performed other checksum-based
    137    /// verification during the `download` step, but this is intended to be run
    138    /// just before a crate is compiled so it may perform more expensive checks
    139    /// which may not be cacheable.
    140    fn verify(&self, _pkg: PackageId) -> CargoResult<()> {
    141        Ok(())
    142    }
    143
    144    /// Describes this source in a human readable fashion, used for display in
    145    /// resolver error messages currently.
    146    fn describe(&self) -> String;
    147
    148    /// Returns whether a source is being replaced by another here.
    149    ///
    150    /// Builtin replacement of `crates.io` doesn't count as replacement here.
    151    fn is_replaced(&self) -> bool {
    152        false
    153    }
    154
    155    /// Add a number of crates that should be whitelisted for showing up during
    156    /// queries, even if they are yanked. Currently only applies to registry
    157    /// sources.
    158    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]);
    159
    160    /// Query if a package is yanked. Only registry sources can mark packages
    161    /// as yanked. This ignores the yanked whitelist.
    162    fn is_yanked(&mut self, _pkg: PackageId) -> Poll<CargoResult<bool>>;
    163
    164    /// Block until all outstanding [`Poll::Pending`] requests are [`Poll::Ready`].
    165    ///
    166    /// After calling this function, the source should return `Poll::Ready` for
    167    /// any queries that previously returned `Poll::Pending`.
    168    ///
    169    /// If no queries previously returned `Poll::Pending`, and [`Source::invalidate_cache`]
    170    /// was not called, this function should be a no-op.
    171    fn block_until_ready(&mut self) -> CargoResult<()>;
    172}
    173
    174/// Defines how a dependency query will be performed for a [`Source`].
    175#[derive(Copy, Clone, PartialEq, Eq)]
    176pub enum QueryKind {
    177    /// A query for packages exactly matching the given dependency requirement.
    178    ///
    179    /// Each source gets to define what `exact` means for it.
    180    Exact,
    181    /// A query for packages close to the given dependency requirement.
    182    ///
    183    /// Each source gets to define what `close` means for it.
    184    ///
    185    /// Path/Git sources may return all dependencies that are at that URI,
    186    /// whereas an `Registry` source may return dependencies that are yanked or invalid.
    187    RejectedVersions,
    188    /// A query for packages close to the given dependency requirement.
    189    ///
    190    /// Each source gets to define what `close` means for it.
    191    ///
    192    /// Path/Git sources may return all dependencies that are at that URI,
    193    /// whereas an `Registry` source may return dependencies that have the same
    194    /// canonicalization.
    195    AlternativeNames,
    196    /// Match a dependency in all ways and will normalize the package name.
    197    /// Each source defines what normalizing means.
    198    Normalized,
    199}
    200
    201/// A download status that represents if a [`Package`] has already been
    202/// downloaded, or if not then a location to download.
    203pub enum MaybePackage {
    204    /// The [`Package`] is already downloaded.
    205    Ready(Package),
    206    /// Not yet downloaded. Here is the URL to download the [`Package`] from.
    207    Download {
    208        /// URL to download the content.
    209        url: String,
    210        /// Text to display to the user of what is being downloaded.
    211        descriptor: String,
    212        /// Authorization data that may be required to attach when downloading.
    213        authorization: Option<String>,
    214    },
    215}
    216
    217/// A blanket implementation forwards all methods to [`Source`].
    218impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
    219    fn source_id(&self) -> SourceId {
    220        (**self).source_id()
    221    }
    222
    223    fn replaced_source_id(&self) -> SourceId {
    224        (**self).replaced_source_id()
    225    }
    226
    227    fn supports_checksums(&self) -> bool {
    228        (**self).supports_checksums()
    229    }
    230
    231    fn requires_precise(&self) -> bool {
    232        (**self).requires_precise()
    233    }
    234
    235    fn query(
    236        &mut self,
    237        dep: &Dependency,
    238        kind: QueryKind,
    239        f: &mut dyn FnMut(IndexSummary),
    240    ) -> Poll<CargoResult<()>> {
    241        (**self).query(dep, kind, f)
    242    }
    243
    244    fn invalidate_cache(&mut self) {
    245        (**self).invalidate_cache()
    246    }
    247
    248    fn set_quiet(&mut self, quiet: bool) {
    249        (**self).set_quiet(quiet)
    250    }
    251
    252    fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
    253        (**self).download(id)
    254    }
    255
    256    fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
    257        (**self).finish_download(id, data)
    258    }
    259
    260    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
    261        (**self).fingerprint(pkg)
    262    }
    263
    264    fn verify(&self, pkg: PackageId) -> CargoResult<()> {
    265        (**self).verify(pkg)
    266    }
    267
    268    fn describe(&self) -> String {
    269        (**self).describe()
    270    }
    271
    272    fn is_replaced(&self) -> bool {
    273        (**self).is_replaced()
    274    }
    275
    276    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
    277        (**self).add_to_yanked_whitelist(pkgs);
    278    }
    279
    280    fn is_yanked(&mut self, pkg: PackageId) -> Poll<CargoResult<bool>> {
    281        (**self).is_yanked(pkg)
    282    }
    283
    284    fn block_until_ready(&mut self) -> CargoResult<()> {
    285        (**self).block_until_ready()
    286    }
    287}
    288
    289/// A blanket implementation forwards all methods to [`Source`].
    290impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
    291    fn source_id(&self) -> SourceId {
    292        (**self).source_id()
    293    }
    294
    295    fn replaced_source_id(&self) -> SourceId {
    296        (**self).replaced_source_id()
    297    }
    298
    299    fn supports_checksums(&self) -> bool {
    300        (**self).supports_checksums()
    301    }
    302
    303    fn requires_precise(&self) -> bool {
    304        (**self).requires_precise()
    305    }
    306
    307    fn query(
    308        &mut self,
    309        dep: &Dependency,
    310        kind: QueryKind,
    311        f: &mut dyn FnMut(IndexSummary),
    312    ) -> Poll<CargoResult<()>> {
    313        (**self).query(dep, kind, f)
    314    }
    315
    316    fn invalidate_cache(&mut self) {
    317        (**self).invalidate_cache()
    318    }
    319
    320    fn set_quiet(&mut self, quiet: bool) {
    321        (**self).set_quiet(quiet)
    322    }
    323
    324    fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
    325        (**self).download(id)
    326    }
    327
    328    fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
    329        (**self).finish_download(id, data)
    330    }
    331
    332    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
    333        (**self).fingerprint(pkg)
    334    }
    335
    336    fn verify(&self, pkg: PackageId) -> CargoResult<()> {
    337        (**self).verify(pkg)
    338    }
    339
    340    fn describe(&self) -> String {
    341        (**self).describe()
    342    }
    343
    344    fn is_replaced(&self) -> bool {
    345        (**self).is_replaced()
    346    }
    347
    348    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
    349        (**self).add_to_yanked_whitelist(pkgs);
    350    }
    351
    352    fn is_yanked(&mut self, pkg: PackageId) -> Poll<CargoResult<bool>> {
    353        (**self).is_yanked(pkg)
    354    }
    355
    356    fn block_until_ready(&mut self) -> CargoResult<()> {
    357        (**self).block_until_ready()
    358    }
    359}
    360
    361/// A [`HashMap`] of [`SourceId`] to `Box<Source>`.
    362#[derive(Default)]
    363pub struct SourceMap<'src> {
    364    map: HashMap<SourceId, Box<dyn Source + 'src>>,
    365}
    366
    367// `impl Debug` on source requires specialization, if even desirable at all.
    368impl<'src> fmt::Debug for SourceMap<'src> {
    369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    370        write!(f, "SourceMap ")?;
    371        f.debug_set().entries(self.map.keys()).finish()
    372    }
    373}
    374
    375impl<'src> SourceMap<'src> {
    376    /// Creates an empty map.
    377    pub fn new() -> SourceMap<'src> {
    378        SourceMap {
    379            map: HashMap::new(),
    380        }
    381    }
    382
    383    /// Like `HashMap::get`.
    384    pub fn get(&self, id: SourceId) -> Option<&(dyn Source + 'src)> {
    385        self.map.get(&id).map(|s| s.as_ref())
    386    }
    387
    388    /// Like `HashMap::get_mut`.
    389    pub fn get_mut(&mut self, id: SourceId) -> Option<&mut (dyn Source + 'src)> {
    390        self.map.get_mut(&id).map(|s| s.as_mut())
    391    }
    392
    393    /// Like `HashMap::insert`, but derives the [`SourceId`] key from the [`Source`].
    394    pub fn insert(&mut self, source: Box<dyn Source + 'src>) {
    395        let id = source.source_id();
    396        self.map.insert(id, source);
    397    }
    398
    399    /// Like `HashMap::len`.
    400    pub fn len(&self) -> usize {
    401        self.map.len()
    402    }
    403
    404    /// Like `HashMap::iter_mut`.
    405    pub fn sources_mut<'a>(
    406        &'a mut self,
    407    ) -> impl Iterator<Item = (&'a SourceId, &'a mut (dyn Source + 'src))> {
    408        self.map.iter_mut().map(|(a, b)| (a, &mut **b))
    409    }
    410
    411    /// Merge the given map into self.
    412    pub fn add_source_map(&mut self, other: SourceMap<'src>) {
    413        for (key, value) in other.map {
    414            self.map.entry(key).or_insert(value);
    415        }
    416    }
    417}