Skip to content

Commit d68eeb0

Browse files
committed
feat(source): wrap PathBuf with PathEntry
This gives us more room to store file metadata. For example, knowing a source file is a symlink and resolving it when packaging.
1 parent 0276088 commit d68eeb0

File tree

5 files changed

+113
-16
lines changed

5 files changed

+113
-16
lines changed

src/cargo/ops/cargo_package/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::core::Workspace;
1616
use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId};
1717
use crate::ops::lockfile::LOCKFILE_NAME;
1818
use crate::ops::registry::{infer_registry, RegistryOrIndex};
19+
use crate::sources::path::PathEntry;
1920
use crate::sources::registry::index::{IndexPackage, RegistryDependency};
2021
use crate::sources::{PathSource, CRATES_IO_REGISTRY};
2122
use crate::util::cache_lock::CacheLockMode;
@@ -396,7 +397,7 @@ fn prepare_archive(
396397
fn build_ar_list(
397398
ws: &Workspace<'_>,
398399
pkg: &Package,
399-
src_files: Vec<PathBuf>,
400+
src_files: Vec<PathEntry>,
400401
vcs_info: Option<vcs::VcsInfo>,
401402
) -> CargoResult<Vec<ArchiveFile>> {
402403
let mut result = HashMap::new();
@@ -420,7 +421,7 @@ fn build_ar_list(
420421
.push(ArchiveFile {
421422
rel_path: rel_path.to_owned(),
422423
rel_str: rel_str.to_owned(),
423-
contents: FileContents::OnDisk(src_file.clone()),
424+
contents: FileContents::OnDisk(src_file.to_path_buf()),
424425
});
425426
}
426427
}

src/cargo/ops/cargo_package/vcs.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use serde::Serialize;
99
use tracing::debug;
1010

1111
use crate::core::Package;
12+
use crate::sources::PathEntry;
1213
use crate::CargoResult;
1314
use crate::GlobalContext;
1415

@@ -41,7 +42,7 @@ pub struct GitVcsInfo {
4142
#[tracing::instrument(skip_all)]
4243
pub fn check_repo_state(
4344
p: &Package,
44-
src_files: &[PathBuf],
45+
src_files: &[PathEntry],
4546
gctx: &GlobalContext,
4647
opts: &PackageOpts<'_>,
4748
) -> CargoResult<Option<VcsInfo>> {
@@ -114,7 +115,7 @@ pub fn check_repo_state(
114115
fn git(
115116
pkg: &Package,
116117
gctx: &GlobalContext,
117-
src_files: &[PathBuf],
118+
src_files: &[PathEntry],
118119
repo: &git2::Repository,
119120
opts: &PackageOpts<'_>,
120121
) -> CargoResult<Option<GitVcsInfo>> {
@@ -136,6 +137,7 @@ fn git(
136137
let mut dirty_src_files: Vec<_> = src_files
137138
.iter()
138139
.filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path)))
140+
.map(|p| p.as_path_buf())
139141
.chain(dirty_metadata_paths(pkg, repo)?.iter())
140142
.map(|path| {
141143
pathdiff::diff_paths(path, cwd)

src/cargo/ops/vendor.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::core::shell::Verbosity;
22
use crate::core::{GitReference, Package, Workspace};
33
use crate::ops;
44
use crate::sources::path::PathSource;
5+
use crate::sources::PathEntry;
56
use crate::sources::CRATES_IO_REGISTRY;
67
use crate::util::cache_lock::CacheLockMode;
78
use crate::util::{try_canonicalize, CargoResult, GlobalContext};
@@ -315,13 +316,14 @@ fn sync(
315316
fn cp_sources(
316317
pkg: &Package,
317318
src: &Path,
318-
paths: &[PathBuf],
319+
paths: &[PathEntry],
319320
dst: &Path,
320321
cksums: &mut BTreeMap<String, String>,
321322
tmp_buf: &mut [u8],
322323
gctx: &GlobalContext,
323324
) -> CargoResult<()> {
324325
for p in paths {
326+
let p = p.as_path();
325327
let relative = p.strip_prefix(&src).unwrap();
326328

327329
match relative.to_str() {

src/cargo/sources/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
pub use self::config::SourceConfigMap;
3030
pub use self::directory::DirectorySource;
3131
pub use self::git::GitSource;
32+
pub use self::path::PathEntry;
3233
pub use self::path::PathSource;
3334
pub use self::path::RecursivePathSource;
3435
pub use self::registry::{

src/cargo/sources/path.rs

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl<'gctx> PathSource<'gctx> {
9494
/// use other methods like `.gitignore`, `package.include`, or
9595
/// `package.exclude` to filter the list of files.
9696
#[tracing::instrument(skip_all)]
97-
pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
97+
pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathEntry>> {
9898
list_files(pkg, self.gctx)
9999
}
100100

@@ -278,7 +278,7 @@ impl<'gctx> RecursivePathSource<'gctx> {
278278
/// are relevant for building this package, but it also contains logic to
279279
/// use other methods like `.gitignore`, `package.include`, or
280280
/// `package.exclude` to filter the list of files.
281-
pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
281+
pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathEntry>> {
282282
list_files(pkg, self.gctx)
283283
}
284284

@@ -404,6 +404,84 @@ impl<'gctx> Source for RecursivePathSource<'gctx> {
404404
}
405405
}
406406

407+
/// Type that abstracts over [`gix::dir::entry::Kind`] and [`fs::FileType`].
408+
#[derive(Debug, Clone, Copy)]
409+
enum FileType {
410+
File,
411+
Dir,
412+
Symlink,
413+
Other,
414+
}
415+
416+
impl From<fs::FileType> for FileType {
417+
fn from(value: fs::FileType) -> Self {
418+
if value.is_file() {
419+
FileType::File
420+
} else if value.is_dir() {
421+
FileType::Dir
422+
} else if value.is_symlink() {
423+
FileType::Symlink
424+
} else {
425+
FileType::Other
426+
}
427+
}
428+
}
429+
430+
impl From<gix::dir::entry::Kind> for FileType {
431+
fn from(value: gix::dir::entry::Kind) -> Self {
432+
use gix::dir::entry::Kind;
433+
match value {
434+
Kind::Untrackable => FileType::Other,
435+
Kind::File => FileType::File,
436+
Kind::Symlink => FileType::Symlink,
437+
Kind::Directory | Kind::Repository => FileType::Dir,
438+
}
439+
}
440+
}
441+
442+
/// [`PathBuf`] with extra metadata.
443+
#[derive(Clone)]
444+
pub struct PathEntry {
445+
path: PathBuf,
446+
ty: FileType,
447+
}
448+
449+
impl PathEntry {
450+
pub fn into_path_buf(self) -> PathBuf {
451+
self.path
452+
}
453+
454+
pub fn as_path_buf(&self) -> &PathBuf {
455+
&self.path
456+
}
457+
458+
pub fn is_file(&self) -> bool {
459+
matches!(self.ty, FileType::File)
460+
}
461+
462+
pub fn is_dir(&self) -> bool {
463+
matches!(self.ty, FileType::Dir)
464+
}
465+
466+
pub fn is_symlink(&self) -> bool {
467+
matches!(self.ty, FileType::Symlink)
468+
}
469+
}
470+
471+
impl fmt::Debug for PathEntry {
472+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
473+
self.path.fmt(f)
474+
}
475+
}
476+
477+
impl std::ops::Deref for PathEntry {
478+
type Target = PathBuf;
479+
480+
fn deref(&self) -> &Self::Target {
481+
&self.path
482+
}
483+
}
484+
407485
fn first_package<'p>(
408486
pkg_id: PackageId,
409487
pkgs: &'p Vec<Package>,
@@ -446,7 +524,7 @@ fn first_package<'p>(
446524
/// are relevant for building this package, but it also contains logic to
447525
/// use other methods like `.gitignore`, `package.include`, or
448526
/// `package.exclude` to filter the list of files.
449-
pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathBuf>> {
527+
pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathEntry>> {
450528
_list_files(pkg, gctx).with_context(|| {
451529
format!(
452530
"failed to determine list of files in {}",
@@ -456,7 +534,7 @@ pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathBu
456534
}
457535

458536
/// See [`PathSource::list_files`].
459-
fn _list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathBuf>> {
537+
fn _list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathEntry>> {
460538
let root = pkg.root();
461539
let no_include_option = pkg.manifest().include().is_empty();
462540
let git_repo = if no_include_option {
@@ -580,7 +658,7 @@ fn list_files_gix(
580658
repo: &gix::Repository,
581659
filter: &dyn Fn(&Path, bool) -> bool,
582660
gctx: &GlobalContext,
583-
) -> CargoResult<Vec<PathBuf>> {
661+
) -> CargoResult<Vec<PathEntry>> {
584662
debug!("list_files_gix {}", pkg.package_id());
585663
let options = repo
586664
.dirwalk_options()?
@@ -619,7 +697,7 @@ fn list_files_gix(
619697
vec![include, exclude]
620698
};
621699

622-
let mut files = Vec::<PathBuf>::new();
700+
let mut files = Vec::<PathEntry>::new();
623701
let mut subpackages_found = Vec::new();
624702
for item in repo
625703
.dirwalk_iter(index.clone(), pathspec, Default::default(), options)?
@@ -701,7 +779,10 @@ fn list_files_gix(
701779
} else if (filter)(&file_path, is_dir) {
702780
assert!(!is_dir);
703781
trace!(" found {}", file_path.display());
704-
files.push(file_path);
782+
files.push(PathEntry {
783+
path: file_path,
784+
ty: kind.map(Into::into).unwrap_or(FileType::Other),
785+
});
705786
}
706787
}
707788

@@ -715,7 +796,7 @@ fn list_files_gix(
715796
/// is not tracked under a Git repository.
716797
fn list_files_walk(
717798
path: &Path,
718-
ret: &mut Vec<PathBuf>,
799+
ret: &mut Vec<PathEntry>,
719800
is_root: bool,
720801
filter: &dyn Fn(&Path, bool) -> bool,
721802
gctx: &GlobalContext,
@@ -756,7 +837,14 @@ fn list_files_walk(
756837
Ok(entry) => {
757838
let file_type = entry.file_type();
758839
if file_type.is_file() || file_type.is_symlink() {
759-
ret.push(entry.into_path());
840+
// We follow_links(true) here so check if entry was created from a symlink
841+
let ty = if entry.path_is_symlink() {
842+
FileType::Symlink
843+
} else {
844+
entry.file_type().into()
845+
};
846+
let path = entry.into_path();
847+
ret.push(PathEntry { path, ty });
760848
}
761849
}
762850
Err(err) if err.loop_ancestor().is_some() => {
@@ -770,7 +858,10 @@ fn list_files_walk(
770858
// Otherwise, simply recover from it.
771859
// Don't worry about error skipping here, the callers would
772860
// still hit the IO error if they do access it thereafter.
773-
Some(path) => ret.push(path.to_path_buf()),
861+
Some(path) => ret.push(PathEntry {
862+
path: path.to_path_buf(),
863+
ty: FileType::Other,
864+
}),
774865
None => return Err(err.into()),
775866
},
776867
}
@@ -801,7 +892,7 @@ fn last_modified_file(
801892
let mtime = paths::mtime(&file).unwrap_or_else(|_| FileTime::zero());
802893
if mtime > max {
803894
max = mtime;
804-
max_path = file;
895+
max_path = file.into_path_buf();
805896
}
806897
}
807898
trace!("last modified file {}: {}", path.display(), max);

0 commit comments

Comments
 (0)