use std::sync::atomic::{AtomicBool, Ordering};
pub const FAILURE: i32 = 1;
pub const USAGE: i32 = 2;
pub const NOTHING_TO_DO: i32 = 3;
#[derive(Debug)]
pub struct Usage(pub String);
impl std::fmt::Display for Usage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Usage {}
pub fn usage(message: impl Into<String>) -> anyhow::Error {
Usage(message.into()).into()
}
static NOTHING_DONE: AtomicBool = AtomicBool::new(false);
pub fn nothing_to_do() {
NOTHING_DONE.store(true, Ordering::Relaxed);
}
pub fn success_code() -> i32 {
if NOTHING_DONE.load(Ordering::Relaxed) {
NOTHING_TO_DO
} else {
0
}
}
pub fn code(failure: &anyhow::Error) -> i32 {
if failure.chain().any(|cause| cause.is::<Usage>()) {
USAGE
} else {
FAILURE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_usage_error_keeps_its_code_under_context() {
let failure = usage("pass --module").context("build");
assert_eq!(code(&failure), USAGE);
assert_eq!(code(&anyhow::anyhow!("boom")), FAILURE);
}
}