use anyhow::Error;
use clap::Parser;
use crossbeam_channel::bounded;
use ignore::DirEntry;
use ignore::WalkState;
use itertools::Itertools;
use std::io;
use std::io::{BufWriter, Write};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
mod args;
mod filetype;
mod glob;
mod interrupt;
mod regex;
mod walk;
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> Result<(), Error> {
let args = args::Args::parse();
let shutdown = Arc::new(AtomicBool::new(false));
interrupt::reset_sigpipe();
interrupt::setup_interrupt_handler(&shutdown)?;
let glob_name =
glob::build_glob_set(args.name.as_ref(), args.case_insensitive)?;
let glob_enabled = args.name.is_some();
let regex_name =
regex::build_regex_set(args.regex.as_ref(), args.case_insensitive)?;
let regex_enabled = args.regex.is_some();
let (tx, rx) = bounded::<DirEntry>(16 * (args.threads - 1));
let print_thread = thread::spawn(move || {
let mut stdout =
BufWriter::with_capacity(256 * 1024, io::stdout().lock());
let mut line_buf: Vec<u8> = Vec::with_capacity(4096);
for dir_entry in rx {
if glob_enabled && !glob_name.is_match(dir_entry.file_name()) {
continue;
}
if regex_enabled
&& !regex_name.is_match(®ex::path_to_bytes(dir_entry.path()))
{
continue;
}
line_buf.clear();
#[cfg(unix)]
line_buf
.extend_from_slice(dir_entry.path().as_os_str().as_bytes());
#[cfg(not(unix))]
line_buf.extend_from_slice(
dir_entry.path().to_string_lossy().as_bytes(),
);
line_buf.push(b'\n');
stdout.write_all(&line_buf).unwrap_or(());
}
stdout.flush().unwrap_or(());
});
let unique_paths =
args.path.iter().unique().cloned().collect::<Vec<PathBuf>>();
let walker = walk::build_walker(&args, &unique_paths);
let filetype_proto = filetype::FileType::new(&args.file_type);
walker.run(|| {
let tx = tx.clone();
let filetype = filetype_proto;
let shutdown = Arc::clone(&shutdown);
Box::new(move |dir_entry| {
if let Ok(dir_entry) = dir_entry {
if filetype.ignore_filetype(&dir_entry) {
return WalkState::Continue;
}
match tx.send(dir_entry) {
Ok(()) => {}
Err(_) => {
return WalkState::Quit;
}
}
}
if shutdown.load(Ordering::Relaxed) {
WalkState::Quit
} else {
WalkState::Continue
}
})
});
drop(tx);
print_thread.join().unwrap();
Ok(())
}