#[macro_use]
pub mod termcolor;
use anyhow::Error;
use clap;
use std::cmp;
use std::fmt::Arguments;
use std::result::Result as StdResult;
#[repr(usize)]
#[derive(Clone, Copy, Eq, Debug)]
pub enum ChatterLevel {
Minimal = 0,
Normal,
}
impl PartialEq for ChatterLevel {
#[inline]
fn eq(&self, other: &ChatterLevel) -> bool {
*self as usize == *other as usize
}
}
impl PartialOrd for ChatterLevel {
#[inline]
fn partial_cmp(&self, other: &ChatterLevel) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ChatterLevel {
#[inline]
fn cmp(&self, other: &ChatterLevel) -> cmp::Ordering {
(*self as usize).cmp(&(*other as usize))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NotificationKind {
Note,
Warning,
Severe,
Fatal,
}
pub trait NotificationBackend {
fn notify(&mut self, kind: NotificationKind, args: Arguments, err: Option<Error>);
}
#[macro_export]
macro_rules! rn_note {
($dest:expr, $( $fmt_args:expr ),*) => {
$dest.notify($crate::notify::NotificationKind::Note, format_args!($( $fmt_args ),*), None)
};
($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => {
$dest.notify($crate::notify::NotificationKind::Note, format_args!($( $fmt_args ),*), Some($err))
};
}
#[macro_export]
macro_rules! rn_warning {
($dest:expr, $( $fmt_args:expr ),*) => {
$dest.notify($crate::notify::NotificationKind::Warning, format_args!($( $fmt_args ),*), None)
};
($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => {
$dest.notify($crate::notify::NotificationKind::Warning, format_args!($( $fmt_args ),*), Some($err))
};
}
#[macro_export]
macro_rules! rn_severe {
($dest:expr, $( $fmt_args:expr ),*) => {
$dest.notify($crate::notify::NotificationKind::Severe, format_args!($( $fmt_args ),*), None)
};
($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => {
$dest.notify($crate::notify::NotificationKind::Severe, format_args!($( $fmt_args ),*), Some($err))
};
}
#[macro_export]
macro_rules! rn_fatal {
($dest:expr, $( $fmt_args:expr ),*) => {
$dest.notify($crate::notify::NotificationKind::Fatal, format_args!($( $fmt_args ),*), None)
};
($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => {
$dest.notify($crate::notify::NotificationKind::Fatal, format_args!($( $fmt_args ),*), Some($err))
};
}
#[derive(Clone, Copy, Debug)]
pub struct NoopNotificationBackend {}
impl NoopNotificationBackend {
pub fn new() -> NoopNotificationBackend {
NoopNotificationBackend {}
}
}
impl Default for NoopNotificationBackend {
fn default() -> Self {
Self::new()
}
}
impl NotificationBackend for NoopNotificationBackend {
fn notify(&mut self, _kind: NotificationKind, _args: Arguments, _err: Option<Error>) {}
}
#[derive(Debug)]
struct NotificationData {
kind: NotificationKind,
text: String,
err: Option<Error>,
}
#[derive(Debug)]
pub struct BufferingNotificationBackend {
buf: Vec<NotificationData>,
}
impl BufferingNotificationBackend {
pub fn new() -> Self {
Self { buf: Vec::new() }
}
pub fn drain<B: NotificationBackend>(mut self, other: &mut B) {
for info in self.buf.drain(..) {
other.notify(info.kind, format_args!("{}", info.text), info.err);
}
}
}
impl Default for BufferingNotificationBackend {
fn default() -> Self {
Self::new()
}
}
impl NotificationBackend for BufferingNotificationBackend {
fn notify(&mut self, kind: NotificationKind, args: Arguments, err: Option<Error>) {
self.buf.push(NotificationData {
kind,
text: format!("{}", args),
err,
});
}
}
pub trait ClapNotificationArgsExt {
fn rubbl_notify_args(self) -> Self;
}
impl ClapNotificationArgsExt for clap::Command {
fn rubbl_notify_args(self) -> Self {
self.arg(
clap::Arg::new("chatter_level")
.long("chatter")
.short('c')
.value_name("LEVEL")
.help("How much chatter to print when running")
.value_parser(["default", "minimal"])
.default_value("default"),
)
}
}
pub fn run_with_notifications<E, F>(matches: clap::ArgMatches, inner: F) -> i32
where
E: Into<Error>,
F: FnOnce(clap::ArgMatches, &mut dyn NotificationBackend) -> StdResult<i32, E>,
{
let chatter = match matches.get_one::<String>("chatter_level").unwrap().as_ref() {
"default" => ChatterLevel::Normal,
"minimal" => ChatterLevel::Minimal,
_ => unreachable!(),
};
let mut tnb = termcolor::TermcolorNotificationBackend::new(chatter);
match inner(matches, &mut tnb) {
Ok(ret) => ret,
Err(e) => {
tnb.bare_error(e);
1
}
}
}