cerm 1.1.1

C-inspired error reporting macros
Documentation
#![doc = include_str!("../README.md")]

#[doc(hidden)]
pub use std::{env, process};

/// Print a diagnostic message to the standard error and exit with a given code.
///
/// This macro is analagous to the BSD [`errx(3)`] C function.  It takes at a
/// minimum two arguments.  The first argument is the code with passed to
/// [`std::process::exit()`] with which we exit the program.  The second and
/// optional additional arguments are passed to the [`eprintln!`] macro.
///
/// When invoked, the given format and arguments are printed to the standard
/// error, prepended by the string `"progname: "`, where `progname` is the
/// program name as defined by the first element in [`std::env::args`].  If for
/// whatever reason no such element exists (which is possible), we default to
/// simply using `"Error"` as the program name.
///
/// If you do not care about specifying a specific exit code and are fine with
/// simply defaulting to `1`, you may prefer to use [`err!`].
///
/// # Panics
///
/// Calls [`eprint!`], [`eprintln!`], and [`std::env::args`] which may all panic
/// if they fail.
///
/// # Examples
///
/// Print a diagnostic in the form `progname: path: error`, and then exit the
/// program with an exit status of 2.
///
/// ```
/// use std::fs;
/// use cerm::err_code;
///
/// let res = match fs::create_dir(&path) {
/// 	Ok(v) => v,
/// 	Err(e) => { err_code!(2, "{}: {}", path, e); }
/// };
/// ```
///
/// [`errx(3)`]: https://man.openbsd.org/err.3
#[macro_export]
macro_rules! err_code {
	($code:expr, $($fmt:tt)+) => {
		eprint!("{}: ", $crate::env::args().next().unwrap_or("Error".into()));
		eprintln!($($fmt)+);
		$crate::process::exit($code);
	};
}

/// The same thing as [`err_code!`], but the exit code is always 1.
///
/// This macro is simply a wrapper around [`err_code!`], but with `1` passed as
/// the first argument; everything else is the same.  For documentation on this
/// macro, read the documentation for [`err_code!`] instead.
///
/// # Panics
///
/// Calls [`err!`] which may panic if it fails.
///
/// # Examples
///
/// Print a diagnostic in the form `progname: path: error`, and then exit the
/// program with an exit status of 1.
///
/// ```
/// use std::fs;
/// use cerm::err_code;
///
/// let res = match fs::create_dir(&path) {
/// 	Ok(v) => v,
/// 	Err(e) => { err!("{}: {}", path, e); }
/// };
/// ```
#[macro_export]
macro_rules! err {
	($($fmt:tt)+) => {
		$crate::err_code!(1, $($fmt)+);
	}
}

/// Print a diagnostic message to the standard error.
///
/// This macro is analagous to the BSD [`warnx(3)`] C function.  It takes the
/// same arguments one would pass to a macro like [`println!`].  In fact, the
/// arguments are passed directly to [`eprintln]`.
///
/// When invoked, the given format and arguments are printed to the standard
/// error, prepended by the string `"progname: "`, where `progname` is the
/// program name as defined by the first element in [`std::env::args`].  If for
/// whatever reason no such element exists (which is possible), we default to
/// simply using `"Error"` as the program name.
///
/// # Panics
///
/// Calls [`eprint!`], [`eprintln!`], and [`std::env::args`] which may all panic
/// if they fail.
///
/// # Examples
///
/// Print a diagnostic in the form `progname: path: error`
///
/// ```
/// use std::fs;
/// use cerm::warn;
///
/// let res = match fs::create_dir(&path) {
/// 	Ok(v) => v,
/// 	Err(e) => {
/// 		warn!("{}: {}", path, e);
/// 		/* … */
/// 	}
/// };
/// ```
///
/// [`errx(3)`]: https://man.openbsd.org/err.3
#[macro_export]
macro_rules! warn {
	($($fmt:tt)+) => {
		eprint!("{}: ", $crate::env::args().next().unwrap_or("Error".into()));
		eprintln!($($fmt)+);
	};
}

/// Require that an expression returns [`Result::Ok`] or [`Option::Some`].
///
/// This macro simplifies error handling when the [`Result::Err`] or
/// [`Option::None`] cases of a [`std::result::Result`] or an
/// [`std::option::Option`] result in logging an error and terminating the
/// program.
///
/// When invoked with a [`std::result::Result`], the macro takes only 1 parameter
/// — the [`std::result::Result`] whose success case you require — and in the
/// case of `Err(v)` returns `v`.  Otherwise the [`err!`] macro is called with
/// the format string `"{e}"` (with `e` being the error).
///
/// Since [`std::option::Option`]s don’t return error values — simply returning
/// [`Option::None` ]— we can’t print a meaningful diagnostic message for you.
/// Therefore you the user also need to provide as additional arguments the same
/// parameters you would pass to an invokation of [`err!`].
///
/// # Panics
///
/// Calls [`err!`] which may panic if it fails.
///
/// # Examples
///
/// Try to take a child process’ standard input and assign it to `ci`.  If the
/// result of `stdin.take()` is [`Option::None` ]then print the given diagnostic
/// message and exit the program using [`err!`].
///
/// ```
/// use cerm::require;
///
/// let ci = require!(
/// 	child.stdin.take(),
/// 	"Failed to open stdin of “{}”",
/// 	cmd.to_string_lossy()
/// );
/// ```
///
///
/// Wait for a child process to terminate, and execute [`err!`] if the call to
/// `child.wait()` fails.  Notice how because `child.wait()` returns a
/// [`std::result::Result`], we only specify one argument.
///
/// ```
/// use cerm::require;
///
/// require!(child.wait());
/// ```
#[macro_export]
macro_rules! require {
	($e:expr) => {
		match $e {
			Ok(v) => v,
			Err(e) => { err!("{e}"); },
		}
	};
	($e:expr, $($fmt:tt)+) => {
		match $e {
			Some(v) => v,
			None => { err!($($fmt)+); },
		}
	};
}