Set the exit code when changes are found

Closes #285
pull/454/head
Wilfred Hughes 2022-12-18 00:28:54 +07:00
parent 7dd6bbd609
commit 6a46237bb0
7 changed files with 70 additions and 4 deletions

@ -15,6 +15,12 @@ directory.
Fixed a race condition where diffing directories would lead to
interleaved output from different files.
### Command Line Interface
Difftastic now sets the exit code if it finds changes. See [usage in
the manual](https://difftastic.wilfred.me.uk/usage.html) for the full
list of exit codes used.
## 0.38 (released 14th November 2022)
### Parsing

@ -53,3 +53,14 @@ are also visible in `--help`.
For example, `DFT_BACKGROUND=light` is equivalent to
`--background=light`. This is useful when using VCS tools like git,
where you are not invoking the `difft` binary directly.
## Exit Codes
0: Difftastic found no syntactic changes (in text files) or no byte
changes (in binary files).
1: Difftastic found syntactic changes (in text files) or byte changes
(in binary files).
2: Difftastic was given invalid arguments, such as file paths that it
couldn't read, or the wrong number of arguments.

@ -0,0 +1,12 @@
/// Successfully ran a diff, found no syntactic changes in text files
/// or byte changes in binary files.
pub const EXIT_SUCCESS: i32 = 0;
/// Successfully ran a diff, found syntactic changes in text files or
/// byte changes in binary files.
pub const EXIT_FOUND_CHANGES: i32 = 1;
/// Invalid arguments given to difftastic. This could be usage errors
/// (e.g. invalid numbers of arguments) or invalid paths (e.g. files
/// we don't have permission to read).
pub const EXIT_BAD_ARGUMENTS: i32 = 2;

@ -10,6 +10,7 @@ use std::{
use rustc_hash::FxHashSet;
use walkdir::WalkDir;
use crate::exit_codes::EXIT_BAD_ARGUMENTS;
use crate::options::FileArgument;
pub fn read_files_or_die(
@ -39,7 +40,7 @@ pub fn read_files_or_die(
if let Err(e) = rhs_res {
eprint_read_error(rhs_path, &e);
}
std::process::exit(1);
std::process::exit(EXIT_BAD_ARGUMENTS);
}
}
}
@ -96,7 +97,7 @@ pub fn read_or_die(path: &Path) -> Vec<u8> {
Ok(src) => src,
Err(e) => {
eprint_read_error(&FileArgument::NamedPath(path.to_path_buf()), &e);
std::process::exit(1);
std::process::exit(EXIT_BAD_ARGUMENTS);
}
}
}

@ -22,6 +22,7 @@
mod constants;
mod diff;
mod display;
mod exit_codes;
mod files;
mod line_parser;
mod lines;
@ -40,6 +41,7 @@ use crate::parse::syntax;
use diff::changes::ChangeMap;
use diff::dijkstra::ExceededGraphLimit;
use display::context::opposite_positions;
use exit_codes::{EXIT_FOUND_CHANGES, EXIT_SUCCESS};
use files::{
guess_content, read_files_or_die, read_or_die, relative_paths_in_either, ProbableFileKind,
};
@ -58,6 +60,8 @@ use diff::sliders::fix_all_sliders;
use options::{DisplayMode, DisplayOptions, FileArgument, Mode, DEFAULT_TAB_WIDTH};
use owo_colors::OwoColorize;
use rayon::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{env, path::Path};
use summary::{DiffResult, FileContent};
use syntax::init_next_prev;
@ -175,6 +179,7 @@ fn main() {
);
}
let encountered_changes = Arc::new(AtomicBool::new(false));
match (&lhs_path, &rhs_path) {
(
options::FileArgument::NamedPath(lhs_path),
@ -186,10 +191,16 @@ fn main() {
// https://github.com/rayon-rs/rayon/issues/210#issuecomment-551319338
let (send, recv) = std::sync::mpsc::sync_channel(1);
let encountered_changes = encountered_changes.clone();
let print_options = display_options.clone();
let printing_thread = std::thread::spawn(move || {
for diff_result in recv.into_iter() {
print_diff_result(&print_options, &diff_result);
if diff_result.has_reportable_change() {
encountered_changes.store(true, Ordering::Relaxed);
}
}
});
@ -221,8 +232,19 @@ fn main() {
language_override,
);
print_diff_result(&display_options, &diff_result);
if diff_result.has_reportable_change() {
encountered_changes.store(true, Ordering::Relaxed);
}
}
}
let exit_code = if encountered_changes.load(Ordering::Relaxed) {
EXIT_FOUND_CHANGES
} else {
EXIT_SUCCESS
};
std::process::exit(exit_code);
}
};
}

@ -6,7 +6,9 @@ use atty::Stream;
use clap::{crate_authors, crate_description, crate_version, Arg, Command};
use const_format::formatcp;
use crate::{display::style::BackgroundColor, parse::guess_language};
use crate::{
display::style::BackgroundColor, exit_codes::EXIT_BAD_ARGUMENTS, parse::guess_language,
};
pub const DEFAULT_BYTE_LIMIT: usize = 1_000_000;
// Chosen experimentally: this is sufficiently many for all the sample
@ -383,7 +385,7 @@ pub fn parse_args() -> Mode {
}
eprintln!("USAGE:\n\n {}\n", USAGE);
eprintln!("For more information try --help");
std::process::exit(1);
std::process::exit(EXIT_BAD_ARGUMENTS);
}
};

@ -23,3 +23,15 @@ pub struct DiffResult {
pub rhs_positions: Vec<MatchedPos>,
pub has_same_bytes: bool,
}
impl DiffResult {
pub fn has_reportable_change(&self) -> bool {
if matches!(self.lhs_src, FileContent::Binary)
|| matches!(self.rhs_src, FileContent::Binary)
{
return !self.has_same_bytes;
}
!self.hunks.is_empty()
}
}