summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Elkin <doug@dougelkin.com>2022-09-06 02:23:32 -0400
committerAbin Simon <abinsimon10@gmail.com>2022-09-12 09:19:42 +0530
commite7fb6f34ed4a383dabe8cd73d00e853613992c02 (patch)
tree27a006eeefb68a5da869b4ae2f369af300dc1fcf
parent496e4be705cd47ec1c0b7e81a36dfc866d394202 (diff)
return POSIX-compatible exit status
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/core.rs19
-rw-r--r--src/display.rs6
-rw-r--r--src/main.rs18
-rw-r--r--src/meta/mod.rs29
-rw-r--r--tests/integration.rs41
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();