diff --git a/src/walk.rs b/src/walk.rs index b3190e0d4..210b6bac5 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -13,8 +13,10 @@ use crate::internal::{opts::FdOptions, osstr_to_bytes, MAX_BUFFER_LENGTH}; use crate::output; use std::borrow::Cow; +use std::convert::TryFrom; use std::error::Error; use std::ffi::OsStr; +use std::fs; use std::io; use std::path::PathBuf; use std::process; @@ -44,6 +46,40 @@ pub enum WorkerResult { Error(ignore::Error), } +/// Converts a WorkerResult to a PathBuf. This is required as in some cases, +/// the underlying `ignore` libary will return an error when we actually +/// still want to show the path. +/// +/// Currently, the cases supported are: +/// - a broken symlink should still be found and the path returned +impl TryFrom for PathBuf { + type Error = ignore::Error; + + fn try_from(worker_result: WorkerResult) -> Result { + match worker_result { + WorkerResult::Entry(value) => return Ok(value), + WorkerResult::Error(err) => { + if let ignore::Error::WithPath { + path, + err: path_err, + } = &err + { + if let ignore::Error::Io(io_err) = &**path_err { + if io_err.kind() == io::ErrorKind::NotFound { + if let Ok(metadata) = fs::symlink_metadata(&path) { + if metadata.file_type().is_symlink() { + return Ok(path.to_path_buf()); + } + } + } + } + } + return Err(err); + } + } + } +} + /// Recursively scan the given search path for files / pathnames matching the pattern. /// /// If the `--exec` argument was supplied, this will create a thread pool for executing @@ -201,8 +237,8 @@ fn spawn_receiver( let mut stdout = stdout.lock(); for worker_result in rx { - match worker_result { - WorkerResult::Entry(value) => { + match PathBuf::try_from(worker_result) { + Ok(value) => { match mode { ReceiverMode::Buffering => { buffer.push(value); @@ -231,7 +267,7 @@ fn spawn_receiver( } } } - WorkerResult::Error(err) => { + Err(err) => { if show_filesystem_errors { print_error!("{}", err); }