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}