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