Skip to content

Commit

Permalink
return POSIX-compatible exit status
Browse files Browse the repository at this point in the history
  • Loading branch information
duhdugg authored and meain committed Sep 12, 2022
1 parent 496e4be commit e7fb6f3
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down Expand Up @@ -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,
Expand All @@ -103,6 +105,7 @@ impl Core {
Ok(meta) => meta,
Err(err) => {
print_error!("{}: {}.", path.display(), err);
exit_code.set_if_greater(ExitCode::MajorIssue);
continue;
}
};
Expand All @@ -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;
}
};
Expand All @@ -131,7 +136,7 @@ impl Core {
}
}

meta_list
(meta_list, exit_code)
}

fn sort(&self, metas: &mut Vec<Meta>) {
Expand Down
6 changes: 6 additions & 0 deletions src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ mod tests {
.unwrap()
.recurse_into(42, &flags)
.unwrap()
.0
.unwrap();
sort(&mut metas, &sort::assemble_sorters(&flags));
let output = tree(
Expand Down Expand Up @@ -633,6 +634,7 @@ mod tests {
.unwrap()
.recurse_into(42, &flags)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
Expand Down Expand Up @@ -672,6 +674,7 @@ mod tests {
.unwrap()
.recurse_into(42, &flags)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
Expand Down Expand Up @@ -710,6 +713,7 @@ mod tests {
.unwrap()
.recurse_into(42, &flags)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
Expand Down Expand Up @@ -739,6 +743,7 @@ mod tests {
.unwrap()
.recurse_into(1, &flags)
.unwrap()
.0
.unwrap();
let output = grid(
&metas,
Expand Down Expand Up @@ -771,6 +776,7 @@ mod tests {
.unwrap()
.recurse_into(1, &flags)
.unwrap()
.0
.unwrap();
let output = grid(
&metas,
Expand Down
18 changes: 17 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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);
}
29 changes: 20 additions & 9 deletions src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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));
}
};

Expand All @@ -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();
Expand All @@ -111,6 +117,7 @@ impl Meta {
Ok(res) => res,
Err(err) => {
print_error!("{}: {}.", path.display(), err);
exit_code.set_if_greater(ExitCode::MinorIssue);
continue;
}
};
Expand All @@ -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;
}
};
Expand All @@ -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) {
Expand Down
41 changes: 41 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -641,6 +643,45 @@ fn test_custom_config_file_parsing() {
.stdout(predicate::str::is_match("folder\n└── file").unwrap());
}

#[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();
Expand Down

0 comments on commit e7fb6f3

Please sign in to comment.