blob: f19bdf311fcf3d43a6fd1f34c26378cc5172745d [file] [log] [blame]
use std::collections::hash_map::HashMap;
use std::fmt;
use crate::core::{Dependency, Package, PackageId, Summary};
use crate::core::package::PackageSet;
use crate::util::{CargoResult, Config};
mod source_id;
pub use self::source_id::{GitReference, SourceId};
/// Something that finds and downloads remote packages based on names and versions.
pub trait Source {
/// Returns the `SourceId` corresponding to this source.
fn source_id(&self) -> SourceId;
/// Returns the replaced `SourceId` corresponding to this source.
fn replaced_source_id(&self) -> SourceId {
self.source_id()
}
/// Returns whether or not this source will return summaries with
/// checksums listed.
fn supports_checksums(&self) -> bool;
/// Returns whether or not this source will return summaries with
/// the `precise` field in the source id listed.
fn requires_precise(&self) -> bool;
/// Attempts to find the packages that match a dependency request.
fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()>;
/// Attempts to find the packages that are close to a dependency request.
/// Each source gets to define what `close` means for it.
/// Path/Git sources may return all dependencies that are at that URI,
/// whereas an `Index` source may return dependencies that have the same canonicalization.
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()>;
fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
let mut ret = Vec::new();
self.query(dep, &mut |s| ret.push(s))?;
Ok(ret)
}
/// Performs any network operations required to get the entire list of all names,
/// versions and dependencies of packages managed by the `Source`.
fn update(&mut self) -> CargoResult<()>;
/// Fetches the full package for each name and version specified.
fn download(&mut self, package: PackageId) -> CargoResult<MaybePackage>;
fn download_now(self: Box<Self>, package: PackageId, config: &Config) -> CargoResult<Package>
where
Self: std::marker::Sized,
{
let mut sources = SourceMap::new();
sources.insert(self);
let pkg_set = PackageSet::new(&[package], sources, config)?;
Ok(pkg_set.get_one(package)?.clone())
}
fn finish_download(&mut self, package: PackageId, contents: Vec<u8>) -> CargoResult<Package>;
/// Generates a unique string which represents the fingerprint of the
/// current state of the source.
///
/// This fingerprint is used to determine the "fresheness" of the source
/// later on. It must be guaranteed that the fingerprint of a source is
/// constant if and only if the output product will remain constant.
///
/// The `pkg` argument is the package which this fingerprint should only be
/// interested in for when this source may contain multiple packages.
fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
/// If this source supports it, verifies the source of the package
/// specified.
///
/// Note that the source may also have performed other checksum-based
/// verification during the `download` step, but this is intended to be run
/// just before a crate is compiled so it may perform more expensive checks
/// which may not be cacheable.
fn verify(&self, _pkg: PackageId) -> CargoResult<()> {
Ok(())
}
/// Describes this source in a human readable fashion, used for display in
/// resolver error messages currently.
fn describe(&self) -> String;
/// Returns whether a source is being replaced by another here.
fn is_replaced(&self) -> bool {
false
}
/// Add a number of crates that should be whitelisted for showing up during
/// queries, even if they are yanked. Currently only applies to registry
/// sources.
fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]);
}
pub enum MaybePackage {
Ready(Package),
Download { url: String, descriptor: String },
}
impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
/// Forwards to `Source::source_id`.
fn source_id(&self) -> SourceId {
(**self).source_id()
}
/// Forwards to `Source::replaced_source_id`.
fn replaced_source_id(&self) -> SourceId {
(**self).replaced_source_id()
}
/// Forwards to `Source::supports_checksums`.
fn supports_checksums(&self) -> bool {
(**self).supports_checksums()
}
/// Forwards to `Source::requires_precise`.
fn requires_precise(&self) -> bool {
(**self).requires_precise()
}
/// Forwards to `Source::query`.
fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
(**self).query(dep, f)
}
/// Forwards to `Source::query`.
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
(**self).fuzzy_query(dep, f)
}
/// Forwards to `Source::update`.
fn update(&mut self) -> CargoResult<()> {
(**self).update()
}
/// Forwards to `Source::download`.
fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
(**self).download(id)
}
fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
(**self).finish_download(id, data)
}
/// Forwards to `Source::fingerprint`.
fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
(**self).fingerprint(pkg)
}
/// Forwards to `Source::verify`.
fn verify(&self, pkg: PackageId) -> CargoResult<()> {
(**self).verify(pkg)
}
fn describe(&self) -> String {
(**self).describe()
}
fn is_replaced(&self) -> bool {
(**self).is_replaced()
}
fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
(**self).add_to_yanked_whitelist(pkgs);
}
}
impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
fn source_id(&self) -> SourceId {
(**self).source_id()
}
fn replaced_source_id(&self) -> SourceId {
(**self).replaced_source_id()
}
fn supports_checksums(&self) -> bool {
(**self).supports_checksums()
}
fn requires_precise(&self) -> bool {
(**self).requires_precise()
}
fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
(**self).query(dep, f)
}
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
(**self).fuzzy_query(dep, f)
}
fn update(&mut self) -> CargoResult<()> {
(**self).update()
}
fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
(**self).download(id)
}
fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
(**self).finish_download(id, data)
}
fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
(**self).fingerprint(pkg)
}
fn verify(&self, pkg: PackageId) -> CargoResult<()> {
(**self).verify(pkg)
}
fn describe(&self) -> String {
(**self).describe()
}
fn is_replaced(&self) -> bool {
(**self).is_replaced()
}
fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
(**self).add_to_yanked_whitelist(pkgs);
}
}
/// A `HashMap` of `SourceId` -> `Box<Source>`.
#[derive(Default)]
pub struct SourceMap<'src> {
map: HashMap<SourceId, Box<dyn Source + 'src>>,
}
// `impl Debug` on source requires specialization, if even desirable at all.
impl<'src> fmt::Debug for SourceMap<'src> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SourceMap ")?;
f.debug_set().entries(self.map.keys()).finish()
}
}
impl<'src> SourceMap<'src> {
/// Creates an empty map.
pub fn new() -> SourceMap<'src> {
SourceMap {
map: HashMap::new(),
}
}
/// Like `HashMap::contains_key`.
pub fn contains(&self, id: SourceId) -> bool {
self.map.contains_key(&id)
}
/// Like `HashMap::get`.
pub fn get(&self, id: SourceId) -> Option<&(dyn Source + 'src)> {
let source = self.map.get(&id);
source.map(|s| {
let s: &(dyn Source + 'src) = &**s;
s
})
}
/// Like `HashMap::get_mut`.
pub fn get_mut(&mut self, id: SourceId) -> Option<&mut (dyn Source + 'src)> {
self.map.get_mut(&id).map(|s| {
let s: &mut (dyn Source + 'src) = &mut **s;
s
})
}
/// Like `HashMap::get`, but first calculates the `SourceId` from a `PackageId`.
pub fn get_by_package_id(&self, pkg_id: PackageId) -> Option<&(dyn Source + 'src)> {
self.get(pkg_id.source_id())
}
/// Like `HashMap::insert`, but derives the `SourceId` key from the `Source`.
pub fn insert(&mut self, source: Box<dyn Source + 'src>) {
let id = source.source_id();
self.map.insert(id, source);
}
/// Like `HashMap::is_empty`.
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
/// Like `HashMap::len`.
pub fn len(&self) -> usize {
self.map.len()
}
/// Like `HashMap::values`.
pub fn sources<'a>(&'a self) -> impl Iterator<Item = &'a Box<dyn Source + 'src>> {
self.map.values()
}
/// Like `HashMap::iter_mut`.
pub fn sources_mut<'a>(
&'a mut self,
) -> impl Iterator<Item = (&'a SourceId, &'a mut (dyn Source + 'src))> {
self.map.iter_mut().map(|(a, b)| (a, &mut **b))
}
}