#[macro_export]
macro_rules! unpack_unit {
($ee: expr) => { $crate::unpack!($ee, ()) };
($ee: expr, $($msg:tt)+) => { $crate::unpack!($ee, (), Debug $($msg)+) };
}
#[macro_export]
macro_rules! unpack_unit_e {
($ee: expr, $($msg:tt)+) => { $crate::unpack!($ee, (), Error $($msg)+) };
}
#[macro_export]
macro_rules! unpack_quiet {
($ee: expr, $ret_val: expr) => {
$crate::unpack!($ee, $ret_val)
};
}
#[macro_export]
macro_rules! unpack_or {
($ee: expr, $ret_val: expr, $($msg:tt)+) => {
$crate::unpack!($ee, $ret_val, Debug $($msg)+)
};
}
#[macro_export]
macro_rules! unpack_or_e {
($ee: expr, $ret_val: expr, $($msg:tt)+) => {
$crate::unpack!($ee, $ret_val, Error $($msg)+)
};
}
#[macro_export]
macro_rules! unpack {
($ee: expr, $ret_val: expr $(, $level: ident $($msg:tt)+)?) => {
match $ee {
Some(item) => item,
None => {
$( log::log!(log::Level::$level, $($msg)+); )?
return $ret_val;
}
}
};
}
#[cfg(test)]
mod tests {
use std::{cell::RefCell, sync::Once};
use rusty_fork::rusty_fork_test;
type TestLogs = Vec<(String, log::Level)>;
thread_local! {
static TEST_LOGS: RefCell<TestLogs> = RefCell::new(Vec::new());
}
static TEST_INIT: Once = Once::new();
struct TestLogger {}
impl TestLogger {
pub fn setup() {
TEST_INIT.call_once(|| {
log::set_logger(&TestLogger {})
.expect("Logging already initialized by another test; run these tests in isolation for proper results");
log::set_max_level(log::LevelFilter::Trace);
});
TEST_LOGS.with(|logs| logs.borrow_mut().clear());
}
pub fn drain_logs() -> TestLogs {
TEST_LOGS.with(|logs| logs.borrow_mut().drain(..).collect())
}
}
impl log::Log for TestLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if !self.enabled(record.metadata()) {
return;
}
TEST_LOGS.with(|logs| {
logs.borrow_mut().push((format!("{}", record.args()), record.level()));
});
}
fn flush(&self) {}
}
rusty_fork_test! {
#[test]
fn test_interface_1() {
let x = || -> Option<i32> {
let x = unpack_quiet!(Some(3), None);
Some(x + 1)
};
let none: Option<i32> = None;
let y = || -> Option<i32> {
let x = unpack_quiet!(none, None);
Some(x + 1)
};
let z = || -> Option<i32> {
let x = unpack_or!(none, None, "log this shit {:?}", none);
Some(x + 1)
};
assert_eq!(x(), Some(4));
assert_eq!(y(), None);
assert_eq!(z(), None);
}
#[test]
fn test_unpack_unit() {
TestLogger::setup();
fn plain_return(expr: Option<usize>, inner: usize) {
let some_val = unpack_unit!(expr);
assert_eq!(some_val, inner);
}
assert_eq!(plain_return(Some(12), 12), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(plain_return(None, 42), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
fn plain_return_and_literal_msg(expr: Option<usize>, inner: usize) {
let some_val = unpack_unit!(expr, "debug msg");
assert_eq!(some_val, inner);
}
assert_eq!(plain_return_and_literal_msg(Some(12), 12), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(plain_return_and_literal_msg(None, 42), ());
assert_eq!(TestLogger::drain_logs(), vec![("debug msg".to_string(), log::Level::Debug)]);
fn plain_return_and_formatted_msg(expr: Option<usize>, inner: usize) {
let some_val = unpack_unit!(expr, "debug msg {} {}", 10, true);
assert_eq!(some_val, inner);
}
assert_eq!(plain_return_and_formatted_msg(Some(12), 12), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(plain_return_and_formatted_msg(None, 42), ());
assert_eq!(TestLogger::drain_logs(), vec![("debug msg 10 true".to_string(), log::Level::Debug)]);
}
#[test]
fn test_unpack_unit_e() {
TestLogger::setup();
fn plain_return_and_literal_msg(expr: Option<usize>, inner: usize) {
let some_val = unpack_unit_e!(expr, "error msg");
assert_eq!(some_val, inner);
}
assert_eq!(plain_return_and_literal_msg(Some(12), 12), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(plain_return_and_literal_msg(None, 42), ());
assert_eq!(TestLogger::drain_logs(), vec![("error msg".to_string(), log::Level::Error)]);
fn plain_return_and_formatted_msg(expr: Option<usize>, inner: usize) {
let some_val = unpack_unit_e!(expr, "error msg {} {}", 10, true);
assert_eq!(some_val, inner);
}
assert_eq!(plain_return_and_formatted_msg(Some(12), 12), ());
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(plain_return_and_formatted_msg(None, 42), ());
assert_eq!(TestLogger::drain_logs(), vec![("error msg 10 true".to_string(), log::Level::Error)]);
}
#[test]
fn test_unpack_quiet() {
TestLogger::setup();
fn return_val(expr: Option<usize>, ret: usize) -> usize {
let some_val = unpack_quiet!(expr, ret);
some_val + 1
}
assert_eq!(return_val(Some(1), 0), 2);
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(return_val(None, 0), 0);
assert_eq!(TestLogger::drain_logs(), vec![]);
}
#[test]
fn test_unpack_or() {
TestLogger::setup();
fn return_val_and_literal_msg(expr: Option<usize>, ret: usize) -> usize {
let some_val = unpack_or!(expr, ret, "debug msg");
some_val + 1
}
assert_eq!(return_val_and_literal_msg(Some(1), 0), 2);
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(return_val_and_literal_msg(None, 0), 0);
assert_eq!(TestLogger::drain_logs(), vec![("debug msg".to_string(), log::Level::Debug)]);
fn return_val_and_formatted_msg(expr: Option<usize>, ret: usize) -> usize {
let some_val = unpack_or!(expr, ret, "debug msg {} {}", 10, true);
some_val + 1
}
assert_eq!(return_val_and_formatted_msg(Some(1), 0), 2);
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(return_val_and_formatted_msg(None, 0), 0);
assert_eq!(TestLogger::drain_logs(), vec![("debug msg 10 true".to_string(), log::Level::Debug)]);
}
#[test]
fn test_unpack_or_e() {
TestLogger::setup();
fn return_val_and_literal_msg(expr: Option<usize>, ret: usize) -> usize {
let some_val = unpack_or_e!(expr, ret, "error msg");
some_val + 1
}
assert_eq!(return_val_and_literal_msg(Some(1), 0), 2);
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(return_val_and_literal_msg(None, 0), 0);
assert_eq!(TestLogger::drain_logs(), vec![("error msg".to_string(), log::Level::Error)]);
fn return_val_and_formatted_msg(expr: Option<usize>, ret: usize) -> usize {
let some_val = unpack_or_e!(expr, ret, "error msg {} {}", 10, true);
some_val + 1
}
assert_eq!(return_val_and_formatted_msg(Some(1), 0), 2);
assert_eq!(TestLogger::drain_logs(), vec![]);
assert_eq!(return_val_and_formatted_msg(None, 0), 0);
assert_eq!(TestLogger::drain_logs(), vec![("error msg 10 true".to_string(), log::Level::Error)]);
}
}
}