Skip to content

Commit b888464

Browse files
committed
refactor(source): Fork PathSource into RecursivePathSource
These are dramatically different and the shared implementation is getting in the way of #10752.
1 parent 463e795 commit b888464

File tree

4 files changed

+201
-16
lines changed

4 files changed

+201
-16
lines changed

src/cargo/ops/resolve.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ use crate::core::PackageSet;
7373
use crate::core::SourceId;
7474
use crate::core::Workspace;
7575
use crate::ops;
76-
use crate::sources::PathSource;
76+
use crate::sources::RecursivePathSource;
7777
use crate::util::cache_lock::CacheLockMode;
7878
use crate::util::errors::CargoResult;
7979
use crate::util::CanonicalUrl;
@@ -453,7 +453,7 @@ pub fn add_overrides<'a>(
453453

454454
for (path, definition) in paths {
455455
let id = SourceId::for_path(&path)?;
456-
let mut source = PathSource::new_recursive(&path, id, ws.gctx());
456+
let mut source = RecursivePathSource::new(&path, id, ws.gctx());
457457
source.update().with_context(|| {
458458
format!(
459459
"failed to update path override `{}` \

src/cargo/sources/git/source.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::sources::source::MaybePackage;
1010
use crate::sources::source::QueryKind;
1111
use crate::sources::source::Source;
1212
use crate::sources::IndexSummary;
13-
use crate::sources::PathSource;
13+
use crate::sources::RecursivePathSource;
1414
use crate::util::cache_lock::CacheLockMode;
1515
use crate::util::errors::CargoResult;
1616
use crate::util::hex::short_hash;
@@ -24,7 +24,7 @@ use tracing::trace;
2424
use url::Url;
2525

2626
/// `GitSource` contains one or more packages gathering from a Git repository.
27-
/// Under the hood it uses [`PathSource`] to discover packages inside the
27+
/// Under the hood it uses [`RecursivePathSource`] to discover packages inside the
2828
/// repository.
2929
///
3030
/// ## Filesystem layout
@@ -79,7 +79,7 @@ pub struct GitSource<'gctx> {
7979
///
8080
/// This gets set to `Some` after the git repo has been checked out
8181
/// (automatically handled via [`GitSource::block_until_ready`]).
82-
path_source: Option<PathSource<'gctx>>,
82+
path_source: Option<RecursivePathSource<'gctx>>,
8383
/// A short string that uniquely identifies the version of the checkout.
8484
///
8585
/// This is typically a 7-character string of the OID hash, automatically
@@ -356,7 +356,7 @@ impl<'gctx> Source for GitSource<'gctx> {
356356
let source_id = self
357357
.source_id
358358
.with_git_precise(Some(actual_rev.to_string()));
359-
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.gctx);
359+
let path_source = RecursivePathSource::new(&checkout_path, source_id, self.gctx);
360360

361361
self.path_source = Some(path_source);
362362
self.short_id = Some(short_id.as_str().into());

src/cargo/sources/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use self::config::SourceConfigMap;
3030
pub use self::directory::DirectorySource;
3131
pub use self::git::GitSource;
3232
pub use self::path::PathSource;
33+
pub use self::path::RecursivePathSource;
3334
pub use self::registry::{
3435
IndexSummary, RegistrySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY,
3536
};

src/cargo/sources/path.rs

Lines changed: 194 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@ use ignore::gitignore::GitignoreBuilder;
2020
use tracing::{debug, trace, warn};
2121
use walkdir::WalkDir;
2222

23-
/// A source represents one or multiple packages gathering from a given root
23+
/// A source that represents a package gathered at the root
2424
/// path on the filesystem.
2525
///
26-
/// It's the cornerstone of every other source --- other implementations
27-
/// eventually need to call `PathSource` to read local packages somewhere on
28-
/// the filesystem.
29-
///
3026
/// It also provides convenient methods like [`PathSource::list_files`] to
3127
/// list all files in a package, given its ability to walk the filesystem.
3228
pub struct PathSource<'gctx> {
@@ -39,7 +35,6 @@ pub struct PathSource<'gctx> {
3935
/// Packages that this sources has discovered.
4036
packages: Vec<Package>,
4137
/// Whether this source should discover nested packages recursively.
42-
/// See [`PathSource::new_recursive`] for more.
4338
recursive: bool,
4439
gctx: &'gctx GlobalContext,
4540
}
@@ -60,6 +55,191 @@ impl<'gctx> PathSource<'gctx> {
6055
}
6156
}
6257

58+
/// Preloads a package for this source. The source is assumed that it has
59+
/// yet loaded any other packages.
60+
pub fn preload_with(&mut self, pkg: Package) {
61+
assert!(!self.updated);
62+
assert!(!self.recursive);
63+
assert!(self.packages.is_empty());
64+
self.updated = true;
65+
self.packages.push(pkg);
66+
}
67+
68+
/// Gets the package on the root path.
69+
pub fn root_package(&mut self) -> CargoResult<Package> {
70+
trace!("root_package; source={:?}", self);
71+
72+
self.update()?;
73+
74+
match self.packages.iter().find(|p| p.root() == &*self.path) {
75+
Some(pkg) => Ok(pkg.clone()),
76+
None => Err(internal(format!(
77+
"no package found in source {:?}",
78+
self.path
79+
))),
80+
}
81+
}
82+
83+
/// Returns the packages discovered by this source. It may walk the
84+
/// filesystem if package information haven't yet updated.
85+
pub fn read_packages(&self) -> CargoResult<Vec<Package>> {
86+
if self.updated {
87+
Ok(self.packages.clone())
88+
} else if self.recursive {
89+
ops::read_packages(&self.path, self.source_id, self.gctx)
90+
} else {
91+
let path = self.path.join("Cargo.toml");
92+
let pkg = ops::read_package(&path, self.source_id, self.gctx)?;
93+
Ok(vec![pkg])
94+
}
95+
}
96+
97+
/// List all files relevant to building this package inside this source.
98+
///
99+
/// This function will use the appropriate methods to determine the
100+
/// set of files underneath this source's directory which are relevant for
101+
/// building `pkg`.
102+
///
103+
/// The basic assumption of this method is that all files in the directory
104+
/// are relevant for building this package, but it also contains logic to
105+
/// use other methods like `.gitignore`, `package.include`, or
106+
/// `package.exclude` to filter the list of files.
107+
pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
108+
list_files(pkg, self.gctx)
109+
}
110+
111+
/// Gets the last modified file in a package.
112+
pub fn last_modified_file(&self, pkg: &Package) -> CargoResult<(FileTime, PathBuf)> {
113+
if !self.updated {
114+
return Err(internal(format!(
115+
"BUG: source `{:?}` was not updated",
116+
self.path
117+
)));
118+
}
119+
last_modified_file(&self.path, pkg, self.gctx)
120+
}
121+
122+
/// Returns the root path of this source.
123+
pub fn path(&self) -> &Path {
124+
&self.path
125+
}
126+
127+
/// Discovers packages inside this source if it hasn't yet done.
128+
pub fn update(&mut self) -> CargoResult<()> {
129+
if !self.updated {
130+
let packages = self.read_packages()?;
131+
self.packages.extend(packages.into_iter());
132+
self.updated = true;
133+
}
134+
135+
Ok(())
136+
}
137+
}
138+
139+
impl<'gctx> Debug for PathSource<'gctx> {
140+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141+
write!(f, "the paths source")
142+
}
143+
}
144+
145+
impl<'gctx> Source for PathSource<'gctx> {
146+
fn query(
147+
&mut self,
148+
dep: &Dependency,
149+
kind: QueryKind,
150+
f: &mut dyn FnMut(IndexSummary),
151+
) -> Poll<CargoResult<()>> {
152+
self.update()?;
153+
for s in self.packages.iter().map(|p| p.summary()) {
154+
let matched = match kind {
155+
QueryKind::Exact => dep.matches(s),
156+
QueryKind::Alternatives => true,
157+
QueryKind::Normalized => dep.matches(s),
158+
};
159+
if matched {
160+
f(IndexSummary::Candidate(s.clone()))
161+
}
162+
}
163+
Poll::Ready(Ok(()))
164+
}
165+
166+
fn supports_checksums(&self) -> bool {
167+
false
168+
}
169+
170+
fn requires_precise(&self) -> bool {
171+
false
172+
}
173+
174+
fn source_id(&self) -> SourceId {
175+
self.source_id
176+
}
177+
178+
fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
179+
trace!("getting packages; id={}", id);
180+
self.update()?;
181+
let pkg = self.packages.iter().find(|pkg| pkg.package_id() == id);
182+
pkg.cloned()
183+
.map(MaybePackage::Ready)
184+
.ok_or_else(|| internal(format!("failed to find {} in path source", id)))
185+
}
186+
187+
fn finish_download(&mut self, _id: PackageId, _data: Vec<u8>) -> CargoResult<Package> {
188+
panic!("no download should have started")
189+
}
190+
191+
fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
192+
let (max, max_path) = self.last_modified_file(pkg)?;
193+
// Note that we try to strip the prefix of this package to get a
194+
// relative path to ensure that the fingerprint remains consistent
195+
// across entire project directory renames.
196+
let max_path = max_path.strip_prefix(&self.path).unwrap_or(&max_path);
197+
Ok(format!("{} ({})", max, max_path.display()))
198+
}
199+
200+
fn describe(&self) -> String {
201+
match self.source_id.url().to_file_path() {
202+
Ok(path) => path.display().to_string(),
203+
Err(_) => self.source_id.to_string(),
204+
}
205+
}
206+
207+
fn add_to_yanked_whitelist(&mut self, _pkgs: &[PackageId]) {}
208+
209+
fn is_yanked(&mut self, _pkg: PackageId) -> Poll<CargoResult<bool>> {
210+
Poll::Ready(Ok(false))
211+
}
212+
213+
fn block_until_ready(&mut self) -> CargoResult<()> {
214+
self.update()
215+
}
216+
217+
fn invalidate_cache(&mut self) {
218+
// Path source has no local cache.
219+
}
220+
221+
fn set_quiet(&mut self, _quiet: bool) {
222+
// Path source does not display status
223+
}
224+
}
225+
226+
/// A source that represents one or multiple packages gathered from a given root
227+
/// path on the filesystem.
228+
pub struct RecursivePathSource<'gctx> {
229+
/// The unique identifier of this source.
230+
source_id: SourceId,
231+
/// The root path of this source.
232+
path: PathBuf,
233+
/// Whether this source has updated all package information it may contain.
234+
updated: bool,
235+
/// Packages that this sources has discovered.
236+
packages: Vec<Package>,
237+
/// Whether this source should discover nested packages recursively.
238+
recursive: bool,
239+
gctx: &'gctx GlobalContext,
240+
}
241+
242+
impl<'gctx> RecursivePathSource<'gctx> {
63243
/// Creates a new source which is walked recursively to discover packages.
64244
///
65245
/// This is similar to the [`PathSource::new`] method except that instead
@@ -68,10 +248,14 @@ impl<'gctx> PathSource<'gctx> {
68248
///
69249
/// Note that this should be used with care and likely shouldn't be chosen
70250
/// by default!
71-
pub fn new_recursive(root: &Path, id: SourceId, gctx: &'gctx GlobalContext) -> Self {
251+
pub fn new(root: &Path, source_id: SourceId, gctx: &'gctx GlobalContext) -> Self {
72252
Self {
253+
source_id,
254+
path: root.to_path_buf(),
255+
updated: false,
256+
packages: Vec::new(),
257+
gctx,
73258
recursive: true,
74-
..Self::new(root, id, gctx)
75259
}
76260
}
77261

@@ -156,13 +340,13 @@ impl<'gctx> PathSource<'gctx> {
156340
}
157341
}
158342

159-
impl<'gctx> Debug for PathSource<'gctx> {
343+
impl<'gctx> Debug for RecursivePathSource<'gctx> {
160344
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
161345
write!(f, "the paths source")
162346
}
163347
}
164348

165-
impl<'gctx> Source for PathSource<'gctx> {
349+
impl<'gctx> Source for RecursivePathSource<'gctx> {
166350
fn query(
167351
&mut self,
168352
dep: &Dependency,

0 commit comments

Comments
 (0)