cargo/core/source/
mod.rs

1use std::collections::hash_map::HashMap;
2use std::fmt;
3
4use crate::core::package::PackageSet;
5use crate::core::{Dependency, Package, PackageId, Summary};
6use crate::util::{CargoResult, Config};
7
8mod source_id;
9
10pub use self::source_id::{GitReference, SourceId};
11
12/// Something that finds and downloads remote packages based on names and versions.
13pub trait Source {
14    /// Returns the `SourceId` corresponding to this source.
15    fn source_id(&self) -> SourceId;
16
17    /// Returns the replaced `SourceId` corresponding to this source.
18    fn replaced_source_id(&self) -> SourceId {
19        self.source_id()
20    }
21
22    /// Returns whether or not this source will return summaries with
23    /// checksums listed.
24    fn supports_checksums(&self) -> bool;
25
26    /// Returns whether or not this source will return summaries with
27    /// the `precise` field in the source id listed.
28    fn requires_precise(&self) -> bool;
29
30    /// Attempts to find the packages that match a dependency request.
31    fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()>;
32
33    /// Attempts to find the packages that are close to a dependency request.
34    /// Each source gets to define what `close` means for it.
35    /// Path/Git sources may return all dependencies that are at that URI,
36    /// whereas an `Index` source may return dependencies that have the same canonicalization.
37    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()>;
38
39    fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
40        let mut ret = Vec::new();
41        self.query(dep, &mut |s| ret.push(s))?;
42        Ok(ret)
43    }
44
45    /// Performs any network operations required to get the entire list of all names,
46    /// versions and dependencies of packages managed by the `Source`.
47    fn update(&mut self) -> CargoResult<()>;
48
49    /// Fetches the full package for each name and version specified.
50    fn download(&mut self, package: PackageId) -> CargoResult<MaybePackage>;
51
52    fn download_now(self: Box<Self>, package: PackageId, config: &Config) -> CargoResult<Package>
53    where
54        Self: std::marker::Sized,
55    {
56        let mut sources = SourceMap::new();
57        sources.insert(self);
58        let pkg_set = PackageSet::new(&[package], sources, config)?;
59        Ok(pkg_set.get_one(package)?.clone())
60    }
61
62    fn finish_download(&mut self, package: PackageId, contents: Vec<u8>) -> CargoResult<Package>;
63
64    /// Generates a unique string which represents the fingerprint of the
65    /// current state of the source.
66    ///
67    /// This fingerprint is used to determine the "fresheness" of the source
68    /// later on. It must be guaranteed that the fingerprint of a source is
69    /// constant if and only if the output product will remain constant.
70    ///
71    /// The `pkg` argument is the package which this fingerprint should only be
72    /// interested in for when this source may contain multiple packages.
73    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
74
75    /// If this source supports it, verifies the source of the package
76    /// specified.
77    ///
78    /// Note that the source may also have performed other checksum-based
79    /// verification during the `download` step, but this is intended to be run
80    /// just before a crate is compiled so it may perform more expensive checks
81    /// which may not be cacheable.
82    fn verify(&self, _pkg: PackageId) -> CargoResult<()> {
83        Ok(())
84    }
85
86    /// Describes this source in a human readable fashion, used for display in
87    /// resolver error messages currently.
88    fn describe(&self) -> String;
89
90    /// Returns whether a source is being replaced by another here.
91    fn is_replaced(&self) -> bool {
92        false
93    }
94
95    /// Add a number of crates that should be whitelisted for showing up during
96    /// queries, even if they are yanked. Currently only applies to registry
97    /// sources.
98    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]);
99
100    /// Query if a package is yanked. Only registry sources can mark packages
101    /// as yanked. This ignores the yanked whitelist.
102    fn is_yanked(&mut self, _pkg: PackageId) -> CargoResult<bool>;
103}
104
105pub enum MaybePackage {
106    Ready(Package),
107    Download { url: String, descriptor: String },
108}
109
110impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
111    /// Forwards to `Source::source_id`.
112    fn source_id(&self) -> SourceId {
113        (**self).source_id()
114    }
115
116    /// Forwards to `Source::replaced_source_id`.
117    fn replaced_source_id(&self) -> SourceId {
118        (**self).replaced_source_id()
119    }
120
121    /// Forwards to `Source::supports_checksums`.
122    fn supports_checksums(&self) -> bool {
123        (**self).supports_checksums()
124    }
125
126    /// Forwards to `Source::requires_precise`.
127    fn requires_precise(&self) -> bool {
128        (**self).requires_precise()
129    }
130
131    /// Forwards to `Source::query`.
132    fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
133        (**self).query(dep, f)
134    }
135
136    /// Forwards to `Source::query`.
137    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
138        (**self).fuzzy_query(dep, f)
139    }
140
141    /// Forwards to `Source::update`.
142    fn update(&mut self) -> CargoResult<()> {
143        (**self).update()
144    }
145
146    /// Forwards to `Source::download`.
147    fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
148        (**self).download(id)
149    }
150
151    fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
152        (**self).finish_download(id, data)
153    }
154
155    /// Forwards to `Source::fingerprint`.
156    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
157        (**self).fingerprint(pkg)
158    }
159
160    /// Forwards to `Source::verify`.
161    fn verify(&self, pkg: PackageId) -> CargoResult<()> {
162        (**self).verify(pkg)
163    }
164
165    fn describe(&self) -> String {
166        (**self).describe()
167    }
168
169    fn is_replaced(&self) -> bool {
170        (**self).is_replaced()
171    }
172
173    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
174        (**self).add_to_yanked_whitelist(pkgs);
175    }
176
177    fn is_yanked(&mut self, pkg: PackageId) -> CargoResult<bool> {
178        (**self).is_yanked(pkg)
179    }
180}
181
182impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
183    fn source_id(&self) -> SourceId {
184        (**self).source_id()
185    }
186
187    fn replaced_source_id(&self) -> SourceId {
188        (**self).replaced_source_id()
189    }
190
191    fn supports_checksums(&self) -> bool {
192        (**self).supports_checksums()
193    }
194
195    fn requires_precise(&self) -> bool {
196        (**self).requires_precise()
197    }
198
199    fn query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
200        (**self).query(dep, f)
201    }
202
203    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut dyn FnMut(Summary)) -> CargoResult<()> {
204        (**self).fuzzy_query(dep, f)
205    }
206
207    fn update(&mut self) -> CargoResult<()> {
208        (**self).update()
209    }
210
211    fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
212        (**self).download(id)
213    }
214
215    fn finish_download(&mut self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
216        (**self).finish_download(id, data)
217    }
218
219    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
220        (**self).fingerprint(pkg)
221    }
222
223    fn verify(&self, pkg: PackageId) -> CargoResult<()> {
224        (**self).verify(pkg)
225    }
226
227    fn describe(&self) -> String {
228        (**self).describe()
229    }
230
231    fn is_replaced(&self) -> bool {
232        (**self).is_replaced()
233    }
234
235    fn add_to_yanked_whitelist(&mut self, pkgs: &[PackageId]) {
236        (**self).add_to_yanked_whitelist(pkgs);
237    }
238
239    fn is_yanked(&mut self, pkg: PackageId) -> CargoResult<bool> {
240        (**self).is_yanked(pkg)
241    }
242}
243
244/// A `HashMap` of `SourceId` -> `Box<Source>`.
245#[derive(Default)]
246pub struct SourceMap<'src> {
247    map: HashMap<SourceId, Box<dyn Source + 'src>>,
248}
249
250// `impl Debug` on source requires specialization, if even desirable at all.
251impl<'src> fmt::Debug for SourceMap<'src> {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "SourceMap ")?;
254        f.debug_set().entries(self.map.keys()).finish()
255    }
256}
257
258impl<'src> SourceMap<'src> {
259    /// Creates an empty map.
260    pub fn new() -> SourceMap<'src> {
261        SourceMap {
262            map: HashMap::new(),
263        }
264    }
265
266    /// Like `HashMap::contains_key`.
267    pub fn contains(&self, id: SourceId) -> bool {
268        self.map.contains_key(&id)
269    }
270
271    /// Like `HashMap::get`.
272    pub fn get(&self, id: SourceId) -> Option<&(dyn Source + 'src)> {
273        let source = self.map.get(&id);
274
275        source.map(|s| {
276            let s: &(dyn Source + 'src) = &**s;
277            s
278        })
279    }
280
281    /// Like `HashMap::get_mut`.
282    pub fn get_mut(&mut self, id: SourceId) -> Option<&mut (dyn Source + 'src)> {
283        self.map.get_mut(&id).map(|s| {
284            let s: &mut (dyn Source + 'src) = &mut **s;
285            s
286        })
287    }
288
289    /// Like `HashMap::get`, but first calculates the `SourceId` from a `PackageId`.
290    pub fn get_by_package_id(&self, pkg_id: PackageId) -> Option<&(dyn Source + 'src)> {
291        self.get(pkg_id.source_id())
292    }
293
294    /// Like `HashMap::insert`, but derives the `SourceId` key from the `Source`.
295    pub fn insert(&mut self, source: Box<dyn Source + 'src>) {
296        let id = source.source_id();
297        self.map.insert(id, source);
298    }
299
300    /// Like `HashMap::is_empty`.
301    pub fn is_empty(&self) -> bool {
302        self.map.is_empty()
303    }
304
305    /// Like `HashMap::len`.
306    pub fn len(&self) -> usize {
307        self.map.len()
308    }
309
310    /// Like `HashMap::values`.
311    pub fn sources<'a>(&'a self) -> impl Iterator<Item = &'a Box<dyn Source + 'src>> {
312        self.map.values()
313    }
314
315    /// Like `HashMap::iter_mut`.
316    pub fn sources_mut<'a>(
317        &'a mut self,
318    ) -> impl Iterator<Item = (&'a SourceId, &'a mut (dyn Source + 'src))> {
319        self.map.iter_mut().map(|(a, b)| (a, &mut **b))
320    }
321
322    /// Merge the given map into self.
323    pub fn add_source_map(&mut self, other: SourceMap<'src>) {
324        for (key, value) in other.map {
325            self.map.entry(key).or_insert(value);
326        }
327    }
328}