mirror of
https://github.com/sharkdp/fd.git
synced 2024-11-18 01:40:34 +01:00
send DirEntries to output instead of just path
This commit is contained in:
parent
81669f4c10
commit
813a802b2c
7 changed files with 88 additions and 80 deletions
62
src/entry.rs
Normal file
62
src/entry.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::{
|
||||||
|
fs::{FileType, Metadata},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
|
enum DirEntryInner {
|
||||||
|
Normal(ignore::DirEntry),
|
||||||
|
BrokenSymlink(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirEntry {
|
||||||
|
inner: DirEntryInner,
|
||||||
|
metadata: OnceCell<Option<Metadata>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
pub fn normal(e: ignore::DirEntry) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: DirEntryInner::Normal(e),
|
||||||
|
metadata: OnceCell::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broken_symlink(path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: DirEntryInner::BrokenSymlink(path),
|
||||||
|
metadata: OnceCell::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
match &self.inner {
|
||||||
|
DirEntryInner::Normal(e) => e.path(),
|
||||||
|
DirEntryInner::BrokenSymlink(pathbuf) => pathbuf.as_path(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_type(&self) -> Option<FileType> {
|
||||||
|
match &self.inner {
|
||||||
|
DirEntryInner::Normal(e) => e.file_type(),
|
||||||
|
DirEntryInner::BrokenSymlink(_) => self.metadata().map(|m| m.file_type()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> Option<&Metadata> {
|
||||||
|
self.metadata
|
||||||
|
.get_or_init(|| match &self.inner {
|
||||||
|
DirEntryInner::Normal(e) => e.metadata().ok(),
|
||||||
|
DirEntryInner::BrokenSymlink(path) => path.symlink_metadata().ok(),
|
||||||
|
})
|
||||||
|
.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn depth(&self) -> Option<usize> {
|
||||||
|
match &self.inner {
|
||||||
|
DirEntryInner::Normal(e) => Some(e.depth()),
|
||||||
|
DirEntryInner::BrokenSymlink(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
|
|
||||||
|
use crate::entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||||
use crate::walk::WorkerResult;
|
use crate::walk::WorkerResult;
|
||||||
|
@ -26,8 +26,8 @@ pub fn job(
|
||||||
|
|
||||||
// Obtain the next result from the receiver, else if the channel
|
// Obtain the next result from the receiver, else if the channel
|
||||||
// has closed, exit from the loop
|
// has closed, exit from the loop
|
||||||
let value: PathBuf = match lock.recv() {
|
let value: DirEntry = match lock.recv() {
|
||||||
Ok(WorkerResult::Entry(path)) => path,
|
Ok(WorkerResult::Entry(val)) => val,
|
||||||
Ok(WorkerResult::Error(err)) => {
|
Ok(WorkerResult::Error(err)) => {
|
||||||
if show_filesystem_errors {
|
if show_filesystem_errors {
|
||||||
print_error(err.to_string());
|
print_error(err.to_string());
|
||||||
|
@ -40,7 +40,7 @@ pub fn job(
|
||||||
// Drop the lock so that other threads can read from the receiver.
|
// Drop the lock so that other threads can read from the receiver.
|
||||||
drop(lock);
|
drop(lock);
|
||||||
// Generate a command, execute it and store its exit code.
|
// Generate a command, execute it and store its exit code.
|
||||||
results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), buffer_output))
|
results.push(cmd.generate_and_execute(value.path(), Arc::clone(&out_perm), buffer_output))
|
||||||
}
|
}
|
||||||
// Returns error in case of any error.
|
// Returns error in case of any error.
|
||||||
merge_exitcodes(results)
|
merge_exitcodes(results)
|
||||||
|
@ -54,7 +54,7 @@ pub fn batch(
|
||||||
limit: usize,
|
limit: usize,
|
||||||
) -> ExitCode {
|
) -> ExitCode {
|
||||||
let paths = rx.iter().filter_map(|value| match value {
|
let paths = rx.iter().filter_map(|value| match value {
|
||||||
WorkerResult::Entry(path) => Some(path),
|
WorkerResult::Entry(val) => Some(val.path().to_owned()),
|
||||||
WorkerResult::Error(err) => {
|
WorkerResult::Error(err) => {
|
||||||
if show_filesystem_errors {
|
if show_filesystem_errors {
|
||||||
print_error(err.to_string());
|
print_error(err.to_string());
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use normpath::PathExt;
|
use normpath::PathExt;
|
||||||
|
|
||||||
use crate::walk;
|
use crate::entry;
|
||||||
|
|
||||||
pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
|
pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
|
||||||
if path.is_absolute() {
|
if path.is_absolute() {
|
||||||
|
@ -51,7 +51,7 @@ pub fn is_executable(_: &fs::Metadata) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(entry: &walk::DirEntry) -> bool {
|
pub fn is_empty(entry: &entry::DirEntry) -> bool {
|
||||||
if let Some(file_type) = entry.file_type() {
|
if let Some(file_type) = entry.file_type() {
|
||||||
if file_type.is_dir() {
|
if file_type.is_dir() {
|
||||||
if let Ok(mut entries) = fs::read_dir(entry.path()) {
|
if let Ok(mut entries) = fs::read_dir(entry.path()) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use crate::entry;
|
||||||
use crate::filesystem;
|
use crate::filesystem;
|
||||||
use crate::walk;
|
|
||||||
|
|
||||||
/// Whether or not to show
|
/// Whether or not to show
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -14,7 +14,7 @@ pub struct FileTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTypes {
|
impl FileTypes {
|
||||||
pub fn should_ignore(&self, entry: &walk::DirEntry) -> bool {
|
pub fn should_ignore(&self, entry: &entry::DirEntry) -> bool {
|
||||||
if let Some(ref entry_type) = entry.file_type() {
|
if let Some(ref entry_type) = entry.file_type() {
|
||||||
(!self.files && entry_type.is_file())
|
(!self.files && entry_type.is_file())
|
||||||
|| (!self.directories && entry_type.is_dir())
|
|| (!self.directories && entry_type.is_dir())
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod app;
|
mod app;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod entry;
|
||||||
mod error;
|
mod error;
|
||||||
mod exec;
|
mod exec;
|
||||||
mod exit_codes;
|
mod exit_codes;
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::path::Path;
|
||||||
use lscolors::{Indicator, LsColors, Style};
|
use lscolors::{Indicator, LsColors, Style};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exit_codes::ExitCode;
|
use crate::exit_codes::ExitCode;
|
||||||
use crate::filesystem::strip_current_dir;
|
use crate::filesystem::strip_current_dir;
|
||||||
|
@ -14,11 +15,12 @@ fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this function is performance critical and can probably be optimized
|
// TODO: this function is performance critical and can probably be optimized
|
||||||
pub fn print_entry<W: Write>(stdout: &mut W, entry: &Path, config: &Config) {
|
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) {
|
||||||
|
let path = entry.path();
|
||||||
let path = if config.strip_cwd_prefix {
|
let path = if config.strip_cwd_prefix {
|
||||||
strip_current_dir(entry)
|
strip_current_dir(path)
|
||||||
} else {
|
} else {
|
||||||
entry
|
path
|
||||||
};
|
};
|
||||||
|
|
||||||
let r = if let Some(ref ls_colors) = config.ls_colors {
|
let r = if let Some(ref ls_colors) = config.ls_colors {
|
||||||
|
|
79
src/walk.rs
79
src/walk.rs
|
@ -1,8 +1,7 @@
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs::{FileType, Metadata};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -13,10 +12,10 @@ use anyhow::{anyhow, Result};
|
||||||
use crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender};
|
use crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender};
|
||||||
use ignore::overrides::OverrideBuilder;
|
use ignore::overrides::OverrideBuilder;
|
||||||
use ignore::{self, WalkBuilder};
|
use ignore::{self, WalkBuilder};
|
||||||
use once_cell::unsync::OnceCell;
|
|
||||||
use regex::bytes::Regex;
|
use regex::bytes::Regex;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exec;
|
use crate::exec;
|
||||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||||
|
@ -36,7 +35,7 @@ enum ReceiverMode {
|
||||||
|
|
||||||
/// The Worker threads can result in a valid entry having PathBuf or an error.
|
/// The Worker threads can result in a valid entry having PathBuf or an error.
|
||||||
pub enum WorkerResult {
|
pub enum WorkerResult {
|
||||||
Entry(PathBuf),
|
Entry(DirEntry),
|
||||||
Error(ignore::Error),
|
Error(ignore::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +186,7 @@ struct ReceiverBuffer<W> {
|
||||||
/// The deadline to switch to streaming mode.
|
/// The deadline to switch to streaming mode.
|
||||||
deadline: Instant,
|
deadline: Instant,
|
||||||
/// The buffer of quickly received paths.
|
/// The buffer of quickly received paths.
|
||||||
buffer: Vec<PathBuf>,
|
buffer: Vec<DirEntry>,
|
||||||
/// Result count.
|
/// Result count.
|
||||||
num_results: usize,
|
num_results: usize,
|
||||||
}
|
}
|
||||||
|
@ -244,20 +243,20 @@ impl<W: Write> ReceiverBuffer<W> {
|
||||||
/// Wait for a result or state change.
|
/// Wait for a result or state change.
|
||||||
fn poll(&mut self) -> Result<(), ExitCode> {
|
fn poll(&mut self) -> Result<(), ExitCode> {
|
||||||
match self.recv() {
|
match self.recv() {
|
||||||
Ok(WorkerResult::Entry(path)) => {
|
Ok(WorkerResult::Entry(dir_entry)) => {
|
||||||
if self.config.quiet {
|
if self.config.quiet {
|
||||||
return Err(ExitCode::HasResults(true));
|
return Err(ExitCode::HasResults(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ReceiverMode::Buffering => {
|
ReceiverMode::Buffering => {
|
||||||
self.buffer.push(path);
|
self.buffer.push(dir_entry);
|
||||||
if self.buffer.len() > MAX_BUFFER_LENGTH {
|
if self.buffer.len() > MAX_BUFFER_LENGTH {
|
||||||
self.stream()?;
|
self.stream()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReceiverMode::Streaming => {
|
ReceiverMode::Streaming => {
|
||||||
self.print(&path)?;
|
self.print(&dir_entry)?;
|
||||||
self.flush()?;
|
self.flush()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,8 +285,8 @@ impl<W: Write> ReceiverBuffer<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Output a path.
|
/// Output a path.
|
||||||
fn print(&mut self, path: &Path) -> Result<(), ExitCode> {
|
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
|
||||||
output::print_entry(&mut self.stdout, path, &self.config);
|
output::print_entry(&mut self.stdout, entry, &self.config);
|
||||||
|
|
||||||
if self.interrupt_flag.load(Ordering::Relaxed) {
|
if self.interrupt_flag.load(Ordering::Relaxed) {
|
||||||
// Ignore any errors on flush, because we're about to exit anyway
|
// Ignore any errors on flush, because we're about to exit anyway
|
||||||
|
@ -313,7 +312,7 @@ impl<W: Write> ReceiverBuffer<W> {
|
||||||
/// Stop looping.
|
/// Stop looping.
|
||||||
fn stop(&mut self) -> Result<(), ExitCode> {
|
fn stop(&mut self) -> Result<(), ExitCode> {
|
||||||
if self.mode == ReceiverMode::Buffering {
|
if self.mode == ReceiverMode::Buffering {
|
||||||
self.buffer.sort();
|
self.buffer.sort_by(|e1, e2| e1.path().cmp(e2.path()));
|
||||||
self.stream()?;
|
self.stream()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,62 +403,6 @@ fn spawn_receiver(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DirEntryInner {
|
|
||||||
Normal(ignore::DirEntry),
|
|
||||||
BrokenSymlink(PathBuf),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DirEntry {
|
|
||||||
inner: DirEntryInner,
|
|
||||||
metadata: OnceCell<Option<Metadata>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirEntry {
|
|
||||||
fn normal(e: ignore::DirEntry) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: DirEntryInner::Normal(e),
|
|
||||||
metadata: OnceCell::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broken_symlink(path: PathBuf) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: DirEntryInner::BrokenSymlink(path),
|
|
||||||
metadata: OnceCell::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> &Path {
|
|
||||||
match &self.inner {
|
|
||||||
DirEntryInner::Normal(e) => e.path(),
|
|
||||||
DirEntryInner::BrokenSymlink(pathbuf) => pathbuf.as_path(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_type(&self) -> Option<FileType> {
|
|
||||||
match &self.inner {
|
|
||||||
DirEntryInner::Normal(e) => e.file_type(),
|
|
||||||
DirEntryInner::BrokenSymlink(_) => self.metadata().map(|m| m.file_type()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn metadata(&self) -> Option<&Metadata> {
|
|
||||||
self.metadata
|
|
||||||
.get_or_init(|| match &self.inner {
|
|
||||||
DirEntryInner::Normal(e) => e.metadata().ok(),
|
|
||||||
DirEntryInner::BrokenSymlink(path) => path.symlink_metadata().ok(),
|
|
||||||
})
|
|
||||||
.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn depth(&self) -> Option<usize> {
|
|
||||||
match &self.inner {
|
|
||||||
DirEntryInner::Normal(e) => Some(e.depth()),
|
|
||||||
DirEntryInner::BrokenSymlink(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_senders(
|
fn spawn_senders(
|
||||||
config: &Arc<Config>,
|
config: &Arc<Config>,
|
||||||
quit_flag: &Arc<AtomicBool>,
|
quit_flag: &Arc<AtomicBool>,
|
||||||
|
@ -610,7 +553,7 @@ fn spawn_senders(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let send_result = tx_thread.send(WorkerResult::Entry(entry_path.to_owned()));
|
let send_result = tx_thread.send(WorkerResult::Entry(entry));
|
||||||
|
|
||||||
if send_result.is_err() {
|
if send_result.is_err() {
|
||||||
return ignore::WalkState::Quit;
|
return ignore::WalkState::Quit;
|
||||||
|
|
Loading…
Reference in a new issue