diff --git a/prebuilt-x86-64-linux/find-images b/prebuilt-x86-64-linux/find-images index fcfe1d6..6602fc1 100755 Binary files a/prebuilt-x86-64-linux/find-images and b/prebuilt-x86-64-linux/find-images differ diff --git a/src/main.rs b/src/main.rs index 2cfdcf0..f4f3be7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ struct CLIArguments { quote: bool, #[structopt(short, long, value_name="EXT", - help="File extensions to filter for (default: jpg jpeg png webp gif heic tiff dpx exr svg)")] + help="File extensions to filter for (default: dpx exr gif heic jpeg jpg png svg tiff webp)")] extensions: Vec, #[structopt(value_name="TARGET", @@ -44,78 +44,96 @@ fn main() -> Result<(), Box>{ }; let valid_extensions: HashSet<&str> = if args.extensions.is_empty() { - ["jpg", "jpeg", "png", "webp", "gif", "heic", "tiff", "dpx", "exr", "svg"].into_iter().collect() + ["dpx", "exr", "gif", "heic", "jpeg", "jpg", "png", "svg", "tiff", "webp"].into_iter().collect() } else { args.extensions.iter().map(|s| s.as_str()).collect() }; - let mut registry: Vec<(std::fs::Metadata,PathBuf)> = Vec::new(); + let mut registry = Registry::new(valid_extensions); - args.targets.into_iter().map(|target| Path::new(&target).to_path_buf() ).for_each( - |path| { - if path.is_file() { - if let Ok(metadata) = std::fs::metadata(&path) { // intentionally not symlink_metadata - register_file(&mut registry, path, metadata, &valid_extensions); - } - } else if path.is_dir() { - register_dir(&mut registry, path, &valid_extensions, args.dohidden); - } - } - ); + registry.populate(args.targets.into_iter().map(|target| Path::new(&target).to_path_buf() ), args.dohidden); if !args.no_sort { - registry.sort_by_key(|(meta,_)| { + registry.sort_by_modified(); + } + + let stdout = std::io::stdout(); + let mut stdout_buffer = std::io::BufWriter::new(stdout.lock()); + registry.write_all(&mut stdout_buffer, args.null, args.quote)?; + + Ok(()) +} + +struct Registry<'a> { + registry: Vec<(std::fs::Metadata,PathBuf)>, + valid_extensions: HashSet<&'a str> +} +impl<'a> Registry<'a> { + pub fn new(valid_extensions: HashSet<&'a str>) -> Self { + Self { registry: Vec::new(), valid_extensions } + } + + pub fn write_all(&self, writer: &mut impl Write, separator_null: bool, quote: bool) -> std::io::Result<()> { + if separator_null { + if quote { for (_,file) in &self.registry { write!(writer, "{}\0", shlex::quote(&file.to_string_lossy()))?; } } + else { for (_,file) in &self.registry { write!(writer, "{}\0", &file.to_string_lossy() )?; } } + } else { + if quote { for (_,file) in &self.registry { writeln!(writer, "{}", shlex::quote(&file.to_string_lossy()))?; } } + else { for (_,file) in &self.registry { writeln!(writer, "{}", &file.to_string_lossy() )?; } } + } + Ok(()) + } + + pub fn sort_by_modified(&mut self) { + self.registry.sort_by_key(|(meta,_)| { meta.modified().ok().unwrap_or_else( || std::time::SystemTime::UNIX_EPOCH, ) }); } - let stdout = std::io::stdout(); - let mut stdout_buffer = std::io::BufWriter::new(stdout.lock()); - - if args.null { - if args.quote { for (_,file) in registry { write!(stdout_buffer, "{}\0", shlex::quote(&file.to_string_lossy()))?; } } - else { for (_,file) in registry { write!(stdout_buffer, "{}\0", &file.to_string_lossy() )?; } } - } else { - if args.quote { for (_,file) in registry { writeln!(stdout_buffer, "{}", shlex::quote(&file.to_string_lossy()))?; } } - else { for (_,file) in registry { writeln!(stdout_buffer, "{}", &file.to_string_lossy() )?; } } - } - - Ok(()) -} - -fn register_file(registry: &mut Vec<(std::fs::Metadata,PathBuf)>, path: PathBuf, metadata: std::fs::Metadata, valid_extensions: &HashSet<&str>) { - if let Some(osstr_ext) = path.extension() { - match osstr_ext.to_str() { - Some(ext) => { - if valid_extensions.contains(ext) { - registry.push((metadata,path)); + pub fn populate(&mut self, source_paths: impl Iterator, dohidden: bool) { + for path in source_paths { + if path.is_file() { + if let Ok(metadata) = std::fs::metadata(&path) { // intentionally not symlink_metadata + self.add_file(path, metadata); } - }, - None => eprintln!( - "Cannot read non-utf-8 file extension: {} on {}", - shlex::quote(&osstr_ext.to_string_lossy()), - shlex::quote(&path.to_string_lossy()) - ) + } else if path.is_dir() { + self.add_dir(path, dohidden); + } } } -} -fn register_dir(registry: &mut Vec<(std::fs::Metadata,PathBuf)>, path: PathBuf, valid_extensions: &HashSet<&str>, dohidden: bool) { - if let Ok(entries) = std::fs::read_dir(path) { - for path in entries.filter_map(|e| e.ok() ).map(|e| e.path() ) { - if !dohidden && path.file_name().map(|name| name.to_string_lossy().starts_with('.')).unwrap_or(true) { // this unwraps to None if the file_name is .. or is root / (neither of which would happen in this scenario) - continue + fn add_file(&mut self, path: PathBuf, metadata: std::fs::Metadata) { + if let Some(osstr_ext) = path.extension() { + match osstr_ext.to_str() { + Some(ext) => if self.valid_extensions.contains(ext) { + self.registry.push((metadata,path)); + }, + None => eprintln!( + "Cannot read non-utf-8 file extension: {} on {}", + shlex::quote(&osstr_ext.to_string_lossy()), + shlex::quote(&path.to_string_lossy()) + ) } - if let Ok(metadata) = std::fs::symlink_metadata(&path) { - if metadata.file_type().is_symlink() { + } + } + + fn add_dir(&mut self, path: PathBuf, dohidden: bool) { + if let Ok(entries) = std::fs::read_dir(path) { + for path in entries.filter_map(|e| e.ok() ).map(|e| e.path() ) { + if !dohidden && path.file_name().map(|name| name.to_string_lossy().starts_with('.')).unwrap_or(true) { // this unwraps to None if the file_name is .. or is root / (neither of which would happen in this scenario) continue } - if path.is_file() { - register_file(registry, path, metadata, &valid_extensions) - } else if path.is_dir() { - register_dir(registry, path, &valid_extensions, dohidden); + if let Ok(metadata) = std::fs::symlink_metadata(&path) { + if metadata.file_type().is_symlink() { + continue + } + if path.is_file() { + self.add_file(path, metadata) + } else if path.is_dir() { + self.add_dir(path, dohidden); + } } } }