use anyhow::Result;
use oxdock_fs::{EntryKind, GuardedPath, WorkspaceFs, to_forward_slashes};
use sha2::{Digest, Sha256};
use std::io::Read;
pub(super) fn copy_entry(fs: &dyn WorkspaceFs, src: &GuardedPath, dst: &GuardedPath) -> Result<()> {
match fs.entry_kind(src)? {
EntryKind::Dir => {
fs.copy_dir_recursive(src, dst)?;
}
EntryKind::File => {
fs.ensure_parent_dir(dst)?;
fs.copy_file(src, dst)?;
}
EntryKind::Symlink => unreachable!("following entry_kind never reports symlinks"),
}
Ok(())
}
pub(super) fn canonical_cwd(fs: &dyn WorkspaceFs, cwd: &GuardedPath) -> Result<String> {
Ok(fs.canonicalize(cwd)?.display().to_string())
}
pub(super) fn describe_dir(
fs: &dyn WorkspaceFs,
root: &GuardedPath,
max_depth: usize,
max_entries: usize,
) -> String {
fn helper(
fs: &dyn WorkspaceFs,
guard_root: &GuardedPath,
path: &GuardedPath,
depth: usize,
max_depth: usize,
left: &mut usize,
out: &mut String,
) {
if *left == 0 {
return;
}
let indent = " ".repeat(depth);
if depth > 0 {
out.push_str(&format!(
"{}{}\n",
indent,
path.as_path()
.file_name()
.unwrap_or_default()
.to_string_lossy()
));
}
if depth >= max_depth {
return;
}
let entries = match fs.read_dir_entries(path) {
Ok(e) => e,
Err(_) => return,
};
let mut names: Vec<_> = entries.into_iter().collect();
names.sort_by_key(|a| a.file_name());
for entry in names {
if *left == 0 {
return;
}
*left -= 1;
let file_type = match entry.file_type() {
Ok(ft) => ft,
Err(_) => continue,
};
let p = entry.path();
let guarded_child = match GuardedPath::new(guard_root.root(), &p) {
Ok(child) => child,
Err(_) => continue,
};
if file_type.is_dir() {
helper(
fs,
guard_root,
&guarded_child,
depth + 1,
max_depth,
left,
out,
);
} else {
out.push_str(&format!(
"{} {}\n",
indent,
entry.file_name().to_string_lossy()
));
}
}
}
let mut out = String::new();
let mut left = max_entries;
helper(fs, root, root, 0, max_depth, &mut left, &mut out);
out
}
pub(super) fn hash_path(
fs: &dyn WorkspaceFs,
path: &GuardedPath,
rel: &str,
hasher: &mut Sha256,
) -> Result<()> {
match fs.entry_kind(path)? {
EntryKind::Dir => {
hasher.update(b"D\0");
hasher.update(rel.as_bytes());
hasher.update(b"\0");
let mut entries = fs.read_dir_entries(path)?;
entries.sort_by_key(|entry| entry.file_name());
for entry in entries {
let name = entry.file_name().to_string_lossy().to_string();
let child = path.join(&name)?;
let child_rel = if rel.is_empty() {
name
} else {
format!("{}/{}", rel, name)
};
let child_rel = to_forward_slashes(&child_rel);
hash_path(fs, &child, &child_rel, hasher)?;
}
}
EntryKind::File => {
let mut reader = fs.open_read(path)?;
if !rel.is_empty() {
hasher.update(b"F\0");
hasher.update(rel.as_bytes());
hasher.update(b"\0");
}
let mut buf = [0u8; super::io::CHUNK_SIZE];
loop {
let n = reader.read(&mut buf)?;
if n == 0 {
break;
}
hasher.update(&buf[..n]);
}
}
EntryKind::Symlink => unreachable!("following entry_kind never reports symlinks"),
}
Ok(())
}