use crate::file_picker::is_known_binary_extension_basename;
use crate::ignore::IGNORED_DIRS;
use crate::types::FileItem;
use crate::walk::{WalkIgnoreRules, WalkOutput};
use parking_lot::Mutex;
use std::path::Path;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use zlob::walk::{WalkBuilder, WalkFlags, WalkMetadata, WalkState};
const PROGRESS_STEP: usize = 13;
#[tracing::instrument(skip_all, name = "zlob walker", level = "info")]
pub(crate) fn walk_collect_files(
base_path: &Path,
is_git_repo: bool,
follow_symlinks: bool,
threads: usize,
synced_files_count: &Arc<AtomicUsize>,
) -> crate::Result<WalkOutput> {
let mut flags = WalkFlags::GITIGNORE;
if !is_git_repo {
flags |= WalkFlags::SKIP_HIDDEN;
}
if follow_symlinks {
flags |= WalkFlags::FOLLOW_SYMLINKS;
}
let mut builder = WalkBuilder::new(base_path)
.map_err(|e| crate::Error::WalkFailed(format!("WalkBuilder::new: {e:?}")))?;
builder
.options(flags)
.threads(threads)
.metadata(WalkMetadata::SIZE | WalkMetadata::MTIME);
if !is_git_repo
&& !IGNORED_DIRS.is_empty()
&& let Err(e) = builder.extra_ignore(IGNORED_DIRS)
{
tracing::warn!(?e, "zlob extra_ignore rejected; walking without it");
}
let collected = Mutex::new((Vec::new(), Vec::new()));
let outcome = match builder.run(|entry| {
if !entry.is_file() {
if entry.is_dir() {
let relative_path = entry.relative_path_lossy();
if !relative_path.is_empty() {
let mut relative_path = relative_path.into_owned();
relative_path.push('/');
collected.lock().1.push(relative_path);
}
}
return WalkState::Continue;
}
let basename = entry.basename().unwrap_or("");
let is_binary = is_known_binary_extension_basename(basename);
let size = entry.size().unwrap_or(0);
let modified = entry
.modified_ns()
.map(|ns| (ns / 1_000_000_000).max(0) as u64)
.unwrap_or(0);
let basename_offset = entry.basename_offset_in_relative_lossy() as u16;
let relative_path = entry.relative_path_lossy().into_owned();
let item = FileItem::new_raw(basename_offset, size, modified, None, is_binary);
let mut guard = collected.lock();
guard.0.push((item, relative_path));
let n = guard.0.len();
drop(guard);
if n % PROGRESS_STEP == 0 {
synced_files_count.store(n, Ordering::Relaxed);
}
WalkState::Continue
}) {
Ok(outcome) => outcome,
Err(e) => {
tracing::error!(?e, "zlob walk failed");
return Err(crate::Error::WalkFailed(format!("{e:?}")));
}
};
let (pairs, dirs) = collected.into_inner();
synced_files_count.store(pairs.len(), Ordering::Relaxed);
let ignore_rules = outcome
.rules()
.is_some()
.then(|| WalkIgnoreRules { inner: outcome });
Ok(WalkOutput {
pairs,
dirs,
ignore_rules,
})
}