Skip to main content

cargo/sources/
source.rs

1//! [`Source`] trait for sources of Cargo packages.
2
3use crate::util::data_structures::HashMap;
4
5use std::fmt;
6use std::rc::Rc;
7
8use crate::core::SourceId;
9use crate::core::{Dependency, Package, PackageId};
10use crate::sources::IndexSummary;
11use crate::util::CargoResult;
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
31#[async_trait::async_trait(?Send)]
32pub trait Source {
33    /// Returns the [`SourceId`] corresponding to this source.
34    fn source_id(&self) -> SourceId;
35
36    /// Returns the replaced [`SourceId`] corresponding to this source.
37    fn replaced_source_id(&self) -> SourceId {
38        self.source_id()
39    }
40
41    /// Returns whether or not this source will return [`IndexSummary`] items with
42    /// checksums listed.
43    fn supports_checksums(&self) -> bool;
44
45    /// Returns whether or not this source will return [`IndexSummary`] items with
46    /// the `precise` field in the [`SourceId`] listed.
47    fn requires_precise(&self) -> bool;
48
49    /// Attempts to find the packages that match a dependency request.
50    ///
51    /// The `f` argument is expected to get called when any [`IndexSummary`] becomes available.
52    async fn query(
53        &self,
54        dep: &Dependency,
55        kind: QueryKind,
56        f: &mut dyn FnMut(IndexSummary),
57    ) -> CargoResult<()>;
58
59    /// Gathers the result from [`Source::query`] as a list of [`IndexSummary`] items
60    /// when they become available.
61    async fn query_vec(&self, dep: &Dependency, kind: QueryKind) -> CargoResult<Vec<IndexSummary>> {
62        let mut ret = Vec::new();
63        self.query(dep, kind, &mut |s| ret.push(s))
64            .await
65            .map(|()| ret)
66    }
67
68    /// Ensure that the source is fully up-to-date for the current session on the next query.
69    fn invalidate_cache(&self);
70
71    /// If quiet, the source should not display any progress or status messages.
72    fn set_quiet(&mut self, quiet: bool);
73
74    /// Starts the process to fetch a [`Package`] for the given [`PackageId`].
75    ///
76    /// If the source already has the package available on disk, then it
77    /// should return immediately with [`MaybePackage::Ready`] with the
78    /// [`Package`]. Otherwise it should return a [`MaybePackage::Download`]
79    /// to indicate the URL to download the package (this is for remote
80    /// registry sources only).
81    ///
82    /// In the case where [`MaybePackage::Download`] is returned, then the
83    /// package downloader will call [`Source::finish_download`] after the
84    /// download has finished.
85    async fn download(&self, package: PackageId) -> CargoResult<MaybePackage>;
86
87    /// Gives the source the downloaded `.crate` file.
88    ///
89    /// When a source has returned [`MaybePackage::Download`] in the
90    /// [`Source::download`] method, then this function will be called with
91    /// the results of the download of the given URL. The source is
92    /// responsible for saving to disk, and returning the appropriate
93    /// [`Package`].
94    async fn finish_download(&self, pkg_id: PackageId, contents: Vec<u8>) -> CargoResult<Package>;
95
96    /// Generates a unique string which represents the fingerprint of the
97    /// current state of the source.
98    ///
99    /// This fingerprint is used to determine the "freshness" of the source
100    /// later on. It must be guaranteed that the fingerprint of a source is
101    /// constant if and only if the output product will remain constant.
102    ///
103    /// The `pkg` argument is the package which this fingerprint should only be
104    /// interested in for when this source may contain multiple packages.
105    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
106
107    /// If this source supports it, verifies the source of the package
108    /// specified.
109    ///
110    /// Note that the source may also have performed other checksum-based
111    /// verification during the `download` step, but this is intended to be run
112    /// just before a crate is compiled so it may perform more expensive checks
113    /// which may not be cacheable.
114    fn verify(&self, _pkg: PackageId) -> CargoResult<()> {
115        Ok(())
116    }
117
118    /// Describes this source in a human readable fashion, used for display in
119    /// resolver error messages currently.
120    fn describe(&self) -> String;
121
122    /// Returns whether a source is being replaced by another here.
123    ///
124    /// Builtin replacement of `crates.io` doesn't count as replacement here.
125    fn is_replaced(&self) -> bool {
126        false
127    }
128}
129
130/// Defines how a dependency query will be performed for a [`Source`].
131#[derive(Copy, Clone, PartialEq, Eq)]
132pub enum QueryKind {
133    /// A query for packages exactly matching the given dependency requirement.
134    ///
135    /// Each source gets to define what `exact` means for it.
136    Exact,
137    /// A query for packages close to the given dependency requirement.
138    ///
139    /// Each source gets to define what `close` means for it.
140    ///
141    /// Path/Git sources may return all dependencies that are at that URI,
142    /// whereas an `Registry` source may return dependencies that are yanked or invalid.
143    RejectedVersions,
144    /// A query for packages close to the given dependency requirement.
145    ///
146    /// Each source gets to define what `close` means for it.
147    ///
148    /// Path/Git sources may return all dependencies that are at that URI,
149    /// whereas an `Registry` source may return dependencies that have the same
150    /// canonicalization.
151    AlternativeNames,
152    /// Match a dependency in all ways and will normalize the package name.
153    /// Each source defines what normalizing means.
154    Normalized,
155}
156
157/// A download status that represents if a [`Package`] has already been
158/// downloaded, or if not then a location to download.
159pub enum MaybePackage {
160    /// The [`Package`] is already downloaded.
161    Ready(Package),
162    /// Not yet downloaded. Here is the URL to download the [`Package`] from.
163    Download {
164        /// URL to download the content.
165        url: String,
166        /// Text to display to the user of what is being downloaded.
167        descriptor: String,
168        /// Authorization data that may be required to attach when downloading.
169        authorization: Option<String>,
170    },
171}
172
173/// A blanket implementation forwards all methods to [`Source`].
174#[async_trait::async_trait(?Send)]
175impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
176    fn source_id(&self) -> SourceId {
177        (**self).source_id()
178    }
179
180    fn replaced_source_id(&self) -> SourceId {
181        (**self).replaced_source_id()
182    }
183
184    fn supports_checksums(&self) -> bool {
185        (**self).supports_checksums()
186    }
187
188    fn requires_precise(&self) -> bool {
189        (**self).requires_precise()
190    }
191
192    async fn query(
193        &self,
194        dep: &Dependency,
195        kind: QueryKind,
196        f: &mut dyn FnMut(IndexSummary),
197    ) -> CargoResult<()> {
198        (**self).query(dep, kind, f).await
199    }
200
201    fn invalidate_cache(&self) {
202        (**self).invalidate_cache()
203    }
204
205    fn set_quiet(&mut self, quiet: bool) {
206        (**self).set_quiet(quiet)
207    }
208
209    async fn download(&self, id: PackageId) -> CargoResult<MaybePackage> {
210        (**self).download(id).await
211    }
212
213    async fn finish_download(&self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
214        (**self).finish_download(id, data).await
215    }
216
217    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
218        (**self).fingerprint(pkg)
219    }
220
221    fn verify(&self, pkg: PackageId) -> CargoResult<()> {
222        (**self).verify(pkg)
223    }
224
225    fn describe(&self) -> String {
226        (**self).describe()
227    }
228
229    fn is_replaced(&self) -> bool {
230        (**self).is_replaced()
231    }
232}
233
234/// A [`HashMap`] of [`SourceId`] to `Box<Source>`.
235#[derive(Default)]
236pub struct SourceMap<'src> {
237    map: HashMap<SourceId, Rc<dyn Source + 'src>>,
238}
239
240// `impl Debug` on source requires specialization, if even desirable at all.
241impl<'src> fmt::Debug for SourceMap<'src> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        write!(f, "SourceMap ")?;
244        f.debug_set().entries(self.map.keys()).finish()
245    }
246}
247
248impl<'src> SourceMap<'src> {
249    /// Creates an empty map.
250    pub fn new() -> SourceMap<'src> {
251        SourceMap {
252            map: HashMap::default(),
253        }
254    }
255
256    /// Like `HashMap::get`.
257    pub fn get(&self, id: SourceId) -> Option<&Rc<dyn Source + 'src>> {
258        self.map.get(&id)
259    }
260
261    /// Like `HashMap::insert`, but derives the [`SourceId`] key from the [`Source`].
262    pub fn insert(&mut self, source: Box<dyn Source + 'src>) {
263        let id = source.source_id();
264        self.map.insert(id, source.into());
265    }
266
267    /// Like `HashMap::len`.
268    pub fn len(&self) -> usize {
269        self.map.len()
270    }
271
272    /// Like `HashMap::iter`.
273    pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceId, &'a (dyn Source + 'src))> {
274        self.map.iter().map(|(a, b)| (a, &**b))
275    }
276
277    /// Merge the given map into self.
278    pub fn add_source_map(&mut self, other: SourceMap<'src>) {
279        for (key, value) in other.map {
280            self.map.entry(key).or_insert(value);
281        }
282    }
283}