diff options
author | Doug Elkin <doug@dougelkin.com> | 2022-09-06 02:23:32 -0400 |
---|---|---|
committer | Abin Simon <abinsimon10@gmail.com> | 2022-09-12 09:19:42 +0530 |
commit | e7fb6f34ed4a383dabe8cd73d00e853613992c02 (patch) | |
tree | 27a006eeefb68a5da869b4ae2f369af300dc1fcf | |
parent | 496e4be705cd47ec1c0b7e81a36dfc866d394202 (diff) |
return POSIX-compatible exit status
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/core.rs | 19 | ||||
-rw-r--r-- | src/display.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 18 | ||||
-rw-r--r-- | src/meta/mod.rs | 29 | ||||
-rw-r--r-- | tests/integration.rs | 41 |
6 files changed, 97 insertions, 17 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f76d091..4ee25e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix tab completion for paths in ZSH +- Fix POSIX-compatible exit status ## [0.23.0] - 2022-09-05 ### Added diff --git a/src/core.rs b/src/core.rs index 629d6f9..407d42c 100644 --- a/src/core.rs +++ b/src/core.rs @@ -6,7 +6,7 @@ use crate::flags::{ }; use crate::icon::{self, Icons}; use crate::meta::Meta; -use crate::{print_error, print_output, sort}; +use crate::{print_error, print_output, sort, ExitCode}; use std::path::PathBuf; #[cfg(not(target_os = "windows"))] @@ -83,14 +83,16 @@ impl Core { } } - pub fn run(self, paths: Vec<PathBuf>) { - let mut meta_list = self.fetch(paths); + pub fn run(self, paths: Vec<PathBuf>) -> ExitCode { + let (mut meta_list, exit_code) = self.fetch(paths); self.sort(&mut meta_list); - self.display(&meta_list) + self.display(&meta_list); + exit_code } - fn fetch(&self, paths: Vec<PathBuf>) -> Vec<Meta> { + fn fetch(&self, paths: Vec<PathBuf>) -> (Vec<Meta>, ExitCode) { + let mut exit_code = ExitCode::OK; let mut meta_list = Vec::with_capacity(paths.len()); let depth = match self.flags.layout { Layout::Tree { .. } => self.flags.recursion.depth, @@ -103,6 +105,7 @@ impl Core { Ok(meta) => meta, Err(err) => { print_error!("{}: {}.", path.display(), err); + exit_code.set_if_greater(ExitCode::MajorIssue); continue; } }; @@ -111,12 +114,14 @@ impl Core { self.flags.layout == Layout::Tree || self.flags.display != Display::DirectoryOnly; if recurse { match meta.recurse_into(depth, &self.flags) { - Ok(content) => { + Ok((content, path_exit_code)) => { meta.content = content; meta_list.push(meta); + exit_code.set_if_greater(path_exit_code); } Err(err) => { print_error!("lsd: {}: {}\n", path.display(), err); + exit_code.set_if_greater(ExitCode::MinorIssue); continue; } }; @@ -131,7 +136,7 @@ impl Core { } } - meta_list + (meta_list, exit_code) } fn sort(&self, metas: &mut Vec<Meta>) { diff --git a/src/display.rs b/src/display.rs index 472bf26..74788e4 100644 --- a/src/display.rs +++ b/src/display.rs @@ -602,6 +602,7 @@ mod tests { .unwrap() .recurse_into(42, &flags) .unwrap() + .0 .unwrap(); sort(&mut metas, &sort::assemble_sorters(&flags)); let output = tree( @@ -633,6 +634,7 @@ mod tests { .unwrap() .recurse_into(42, &flags) .unwrap() + .0 .unwrap(); let output = tree( &metas, @@ -672,6 +674,7 @@ mod tests { .unwrap() .recurse_into(42, &flags) .unwrap() + .0 .unwrap(); let output = tree( &metas, @@ -710,6 +713,7 @@ mod tests { .unwrap() .recurse_into(42, &flags) .unwrap() + .0 .unwrap(); let output = tree( &metas, @@ -739,6 +743,7 @@ mod tests { .unwrap() .recurse_into(1, &flags) .unwrap() + .0 .unwrap(); let output = grid( &metas, @@ -771,6 +776,7 @@ mod tests { .unwrap() .recurse_into(1, &flags) .unwrap() + .0 .unwrap(); let output = grid( &metas, diff --git a/src/main.rs b/src/main.rs index e17567a..44829e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,6 +42,21 @@ use crate::core::Core; use crate::flags::Flags; use std::path::PathBuf; +#[derive(PartialEq, Eq, PartialOrd, Copy, Clone)] +pub enum ExitCode { + OK, + MinorIssue, + MajorIssue, +} +impl ExitCode { + pub fn set_if_greater(&mut self, code: ExitCode) { + let self_i32 = *self as i32; + let code_i32 = code as i32; + if self_i32 < code_i32 { + *self = code; + } + } +} /// Macro used to avoid panicking when the lsd method is used with a pipe and /// stderr close before our program. #[macro_export] @@ -115,5 +130,6 @@ fn main() { let flags = Flags::configure_from(&matches, &config).unwrap_or_else(|err| err.exit()); let core = Core::new(flags); - core.run(inputs); + let exit_code = core.run(inputs); + std::process::exit(exit_code as i32); } diff --git a/src/meta/mod.rs b/src/meta/mod.rs index b83228b..20c2af3 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -27,7 +27,7 @@ pub use self::symlink::SymLink; pub use crate::icon::Icons; use crate::flags::{Display, Flags, Layout}; -use crate::print_error; +use crate::{print_error, ExitCode}; use std::io::{self, Error, ErrorKind}; use std::path::{Component, Path, PathBuf}; @@ -50,30 +50,34 @@ pub struct Meta { } impl Meta { - pub fn recurse_into(&self, depth: usize, flags: &Flags) -> io::Result<Option<Vec<Meta>>> { + pub fn recurse_into( + &self, + depth: usize, + flags: &Flags, + ) -> io::Result<(Option<Vec<Meta>>, ExitCode)> { if depth == 0 { - return Ok(None); + return Ok((None, ExitCode::OK)); } if flags.display == Display::DirectoryOnly && flags.layout != Layout::Tree { - return Ok(None); + return Ok((None, ExitCode::OK)); } match self.file_type { FileType::Directory { .. } => (), FileType::SymLink { is_dir: true } => { if flags.layout == Layout::OneLine { - return Ok(None); + return Ok((None, ExitCode::OK)); } } - _ => return Ok(None), + _ => return Ok((None, ExitCode::OK)), } let entries = match self.path.read_dir() { Ok(entries) => entries, Err(err) => { print_error!("{}: {}.", self.path.display(), err); - return Ok(None); + return Ok((None, ExitCode::MinorIssue)); } }; @@ -91,6 +95,8 @@ impl Meta { content.push(parent_meta); } + let mut exit_code = ExitCode::OK; + for entry in entries { let entry = entry?; let path = entry.path(); @@ -111,6 +117,7 @@ impl Meta { Ok(res) => res, Err(err) => { print_error!("{}: {}.", path.display(), err); + exit_code.set_if_greater(ExitCode::MinorIssue); continue; } }; @@ -126,9 +133,13 @@ impl Meta { // check dereferencing if flags.dereference.0 || !matches!(entry_meta.file_type, FileType::SymLink { .. }) { match entry_meta.recurse_into(depth - 1, flags) { - Ok(content) => entry_meta.content = content, + Ok((content, rec_exit_code)) => { + entry_meta.content = content; + exit_code.set_if_greater(rec_exit_code); + } Err(err) => { print_error!("{}: {}.", path.display(), err); + exit_code.set_if_greater(ExitCode::MinorIssue); continue; } }; @@ -137,7 +148,7 @@ impl Meta { content.push(entry_meta); } - Ok(Some(content)) + Ok((Some(content), exit_code)) } pub fn calculate_total_size(&mut self) { diff --git a/tests/integration.rs b/tests/integration.rs index 19c4363..39e8e93 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -8,6 +8,8 @@ use std::process::Command; #[cfg(unix)] use std::os::unix::fs; +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; #[test] fn test_runs_okay() { @@ -642,6 +644,45 @@ fn test_custom_config_file_parsing() { } #[test] +fn test_cannot_access_file_exit_status() { + let dir = tempdir(); + let does_not_exist = dir.path().join("does_not_exist"); + + let status = cmd() + .arg("-l") + .arg("--ignore-config") + .arg(does_not_exist) + .status() + .unwrap() + .code() + .unwrap(); + + assert_eq!(status, 2) +} + +#[cfg(unix)] +#[test] +fn test_cannot_access_subdir_exit_status() { + let tmp = tempdir(); + + let readonly = std::fs::Permissions::from_mode(0o400); + tmp.child("d/subdir/onemore").create_dir_all().unwrap(); + + std::fs::set_permissions(tmp.child("d").path().join("subdir"), readonly).unwrap(); + + let status = cmd() + .arg("--tree") + .arg("--ignore-config") + .arg(tmp.child("d").path()) + .status() + .unwrap() + .code() + .unwrap(); + + assert_eq!(status, 1) +} + +#[test] fn test_date_custom_format_supports_nanos_with_length() { let dir = tempdir(); dir.child("one").touch().unwrap(); |