Skip to main content

cargo/sources/
replaced.rs

1use crate::core::{Dependency, Package, PackageId, SourceId};
2use crate::sources::IndexSummary;
3use crate::sources::source::MaybePackage;
4use crate::sources::source::QueryKind;
5use crate::sources::source::Source;
6use crate::util::errors::CargoResult;
7
8/// A source that replaces one source with the other. This manages the [source
9/// replacement] feature.
10///
11/// The implementation is merely redirecting from the original to the replacement.
12///
13/// [source replacement]: https://doc.rust-lang.org/nightly/cargo/reference/source-replacement.html
14pub struct ReplacedSource<'gctx> {
15    /// The identifier of the original source.
16    to_replace: SourceId,
17    /// The identifier of the new replacement source.
18    replace_with: SourceId,
19    inner: Box<dyn Source + 'gctx>,
20}
21
22impl<'gctx> ReplacedSource<'gctx> {
23    /// Creates a replaced source.
24    ///
25    /// The `src` argument is the new replacement source.
26    pub fn new(
27        to_replace: SourceId,
28        replace_with: SourceId,
29        src: Box<dyn Source + 'gctx>,
30    ) -> ReplacedSource<'gctx> {
31        ReplacedSource {
32            to_replace,
33            replace_with,
34            inner: src,
35        }
36    }
37
38    /// Is this source a built-in replacement of crates.io?
39    ///
40    /// Built-in source replacement of crates.io for sparse registry or tests
41    /// should not show messages indicating source replacement.
42    fn is_builtin_replacement(&self) -> bool {
43        self.replace_with.is_crates_io() && self.to_replace.is_crates_io()
44    }
45}
46
47#[async_trait::async_trait(?Send)]
48impl<'gctx> Source for ReplacedSource<'gctx> {
49    fn source_id(&self) -> SourceId {
50        self.to_replace
51    }
52
53    fn replaced_source_id(&self) -> SourceId {
54        self.replace_with
55    }
56
57    fn supports_checksums(&self) -> bool {
58        self.inner.supports_checksums()
59    }
60
61    fn requires_precise(&self) -> bool {
62        self.inner.requires_precise()
63    }
64
65    async fn query(
66        &self,
67        dep: &Dependency,
68        kind: QueryKind,
69        f: &mut dyn FnMut(IndexSummary),
70    ) -> CargoResult<()> {
71        let (replace_with, to_replace) = (self.replace_with, self.to_replace);
72        let dep = dep.clone().map_source(to_replace, replace_with);
73
74        self.inner
75            .query(&dep, kind, &mut |summary| {
76                f(summary.map_summary(|s| s.map_source(replace_with, to_replace)))
77            })
78            .await
79            .map_err(|e| {
80                if self.is_builtin_replacement() {
81                    e
82                } else {
83                    e.context(format!(
84                        "failed to query replaced source {}",
85                        self.to_replace
86                    ))
87                }
88            })
89    }
90
91    fn invalidate_cache(&self) {
92        self.inner.invalidate_cache()
93    }
94
95    fn set_quiet(&mut self, quiet: bool) {
96        self.inner.set_quiet(quiet);
97    }
98
99    fn download(&self, id: PackageId) -> CargoResult<MaybePackage> {
100        let id = id.with_source_id(self.replace_with);
101        let pkg = self.inner.download(id).map_err(|e| {
102            if self.is_builtin_replacement() {
103                e
104            } else {
105                e.context(format!(
106                    "failed to download replaced source {}",
107                    self.to_replace
108                ))
109            }
110        })?;
111        Ok(match pkg {
112            MaybePackage::Ready(pkg) => {
113                MaybePackage::Ready(pkg.map_source(self.replace_with, self.to_replace))
114            }
115            other @ MaybePackage::Download { .. } => other,
116        })
117    }
118
119    fn finish_download(&self, id: PackageId, data: Vec<u8>) -> CargoResult<Package> {
120        let id = id.with_source_id(self.replace_with);
121        let pkg = self.inner.finish_download(id, data).map_err(|e| {
122            if self.is_builtin_replacement() {
123                e
124            } else {
125                e.context(format!(
126                    "failed to download replaced source {}",
127                    self.to_replace
128                ))
129            }
130        })?;
131        Ok(pkg.map_source(self.replace_with, self.to_replace))
132    }
133
134    fn fingerprint(&self, id: &Package) -> CargoResult<String> {
135        self.inner.fingerprint(id)
136    }
137
138    fn verify(&self, id: PackageId) -> CargoResult<()> {
139        let id = id.with_source_id(self.replace_with);
140        self.inner.verify(id)
141    }
142
143    fn describe(&self) -> String {
144        if self.is_builtin_replacement() {
145            self.inner.describe()
146        } else {
147            format!(
148                "{} (which is replacing {})",
149                self.inner.describe(),
150                self.to_replace
151            )
152        }
153    }
154
155    fn is_replaced(&self) -> bool {
156        !self.is_builtin_replacement()
157    }
158
159    fn add_to_yanked_whitelist(&self, pkgs: &[PackageId]) {
160        let pkgs = pkgs
161            .iter()
162            .map(|id| id.with_source_id(self.replace_with))
163            .collect::<Vec<_>>();
164        self.inner.add_to_yanked_whitelist(&pkgs);
165    }
166
167    async fn is_yanked(&self, pkg: PackageId) -> CargoResult<bool> {
168        self.inner.is_yanked(pkg).await
169    }
170}