Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/copper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pistonite-cu"
version = "0.9.0"
version = "0.9.1"
edition = "2024"
description = "Battery-included common utils to speed up development of rust tools"
repository = "https://github.com/Pistonite/cu"
Expand Down
7 changes: 6 additions & 1 deletion packages/copper/src/fs/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,18 @@ fn rec_copy_inefficiently_impl(from: &Path, to: &Path) -> crate::Result<()> {
crate::bail!("target exists and is not a directory");
}

cu::check!(
make_dir_impl(to),
"rec_copy: failed to create target directory"
)?;

// cache paths that are known to be directories in `to`
let mut dir_cache = BTreeSet::new();
let mut walker = crate::fs::walker(from);
walker.include_dir_entries(true);
for entry in walker.walk()? {
let entry = entry?;
let rel_path = cu::check!(entry.rel_path()?, "failed to get relative path for entry")?;
let rel_path = entry.rel_path()?;
if !entry.is_dir() {
let Some(file_name) = entry.file_name() else {
cu::trace!(
Expand Down
35 changes: 17 additions & 18 deletions packages/copper/src/fs/walk2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,12 @@ impl WalkBuilder {
///
/// ```rust,no_run
/// # use pistonite_cu as cu;
/// fn list() -> cu::Result<()> {
/// for entry in cu::fs::walk(".")? {
/// let entry = entry?;
/// cu::info!("depth {}: {}", entry.depth(), entry.path().display());
/// }
/// Ok(())
/// # fn list() -> cu::Result<()> {
/// for entry in cu::fs::walk(".")? {
/// let entry = entry?;
/// cu::info!("depth {}: {}", entry.depth(), entry.path().display());
/// }
/// # Ok(()) }
/// ```
pub struct Walk {
inner: IgnoreWalk,
Expand All @@ -294,6 +293,10 @@ impl Walk {
let (entry, file_type) = loop {
let entry = crate::some!(self.inner.next());
let entry = crate::check!(entry, "walk: failed to read the next entry")?;
if entry.depth() == 0 {
// skip root node
continue;
}
match entry.file_type() {
None => {
// stdin
Expand Down Expand Up @@ -377,17 +380,14 @@ impl WalkEntry {
///
/// ```rust,no_run
/// # use pistonite_cu as cu;
/// fn print_relative() -> cu::Result<()> {
/// for entry in cu::fs::walk("src")? {
/// let entry = entry?;
/// if let Some(rel) = entry.rel_path()? {
/// cu::info!("{}", rel.display());
/// }
/// }
/// Ok(())
/// # fn print_relative() -> cu::Result<()> {
/// for entry in cu::fs::walk("src")? {
/// let entry = entry?;
/// cu::info!("{}", entry.rel_path()?.display());
/// }
/// # Ok(()) }
/// ```
pub fn rel_path(&self) -> cu::Result<Option<PathBuf>> {
pub fn rel_path(&self) -> cu::Result<PathBuf> {
// ensure root is a prefix of inner path
let root_norm = self.root.normalize()?;
// note we cannot normalize the path after join since it might be a symlink
Expand All @@ -409,13 +409,12 @@ impl WalkEntry {
)?;
}
let Some(next) = path_iter.next() else {
// is root
return Ok(None);
cu::bail!("unexpected: walk entry path is same as root");
};
let mut p = PathBuf::new();
p.push(next);
p.extend(path_iter);
Ok(Some(p))
Ok(p)
}

/// Get the file type
Expand Down
66 changes: 24 additions & 42 deletions packages/copper/tests/walk2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,8 @@ fn collect(w: cu::fs::Walk) -> cu::Result<BTreeSet<String>> {
let mut set = BTreeSet::new();
for entry in w {
let entry = entry?;
match entry.rel_path()? {
Some(p) => {
set.insert(norm(&p));
}
None => {
set.insert(".".to_string());
}
}
let rel_path = entry.rel_path()?;
set.insert(norm(&rel_path));
}
Ok(set)
}
Expand Down Expand Up @@ -163,8 +157,7 @@ fn include_dir_entries_true() -> cu::Result<()> {
}
// ...alongside the files...
assert!(got.contains("sub/c.txt"));
// ...and the root entry (rel_path == None).
assert!(got.contains("."), "expected root entry in {got:?}");
assert!(!got.contains("."));
Ok(())
}

Expand Down Expand Up @@ -326,49 +319,38 @@ fn entry_metadata_depth_relpath() -> cu::Result<()> {
let mut b = cu::fs::walker(fx.root());
b.include_dir_entries(true);

let mut saw_root = false;
let mut saw_top_file = false;
let mut saw_nested_file = false;
let mut saw_dir = false;

for entry in b.walk()? {
let entry = entry?;
match entry.rel_path()? {
None => {
// the root entry
assert_eq!(entry.depth(), 0, "root depth should be 0");
assert!(entry.is_dir());
saw_root = true;
assert!(entry.depth() > 0, "should not emit root entry");
let rel = norm(&entry.rel_path()?);
// rel_path must never carry a leading "./"
assert!(!rel.starts_with("./"), "rel_path has leading ./: {rel}");
match rel.as_str() {
"a.txt" => {
assert_eq!(entry.depth(), 1);
assert!(entry.is_file());
assert_eq!(entry.file_name().unwrap(), "a.txt");
entry.metadata()?;
saw_top_file = true;
}
"sub/nested/e.txt" => {
assert_eq!(entry.depth(), 3, "nested file should be depth 3");
assert!(entry.is_file());
saw_nested_file = true;
}
Some(rel) => {
let rel = norm(&rel);
// rel_path must never carry a leading "./"
assert!(!rel.starts_with("./"), "rel_path has leading ./: {rel}");
match rel.as_str() {
"a.txt" => {
assert_eq!(entry.depth(), 1);
assert!(entry.is_file());
assert_eq!(entry.file_name().unwrap(), "a.txt");
entry.metadata()?;
saw_top_file = true;
}
"sub/nested/e.txt" => {
assert_eq!(entry.depth(), 3, "nested file should be depth 3");
assert!(entry.is_file());
saw_nested_file = true;
}
"sub" => {
assert_eq!(entry.depth(), 1);
assert!(entry.is_dir());
saw_dir = true;
}
_ => {}
}
"sub" => {
assert_eq!(entry.depth(), 1);
assert!(entry.is_dir());
saw_dir = true;
}
_ => {}
}
}

assert!(saw_root, "did not observe root entry");
assert!(saw_top_file, "did not observe a.txt");
assert!(saw_nested_file, "did not observe sub/nested/e.txt");
assert!(saw_dir, "did not observe sub dir");
Expand Down