#[cfg(feature = "debug")]
pub use backtrace;
use crate::error::Error;
#[derive(PartialEq, Eq)]
pub enum OutputKind {
Ok,
Error,
Warning,
Info,
Debug,
Trace
}
pub struct OutputRecord {
pub kind: OutputKind,
#[cfg(feature = "debug")]
pub module: String,
#[cfg(feature = "debug")]
pub line: Option<u32>,
pub message: String
}
pub trait CliOutput: Sync {
fn is_enabled(&self, kind: OutputKind) -> bool;
fn log(&self, record: OutputRecord) -> Result<(), Error>;
}
static mut IMPL: Option<&dyn CliOutput> = None;
const MISSING_IMPL_MESSAGE: &str = "Log implementation is not set";
pub fn get_impl() -> &'static dyn CliOutput {
unsafe {
return IMPL.expect(MISSING_IMPL_MESSAGE);
}
}
pub fn set_impl(reference: &'static dyn CliOutput) {
unsafe {
if let Some(_) = IMPL {
panic!("Cannot set log implementation twice"); }
IMPL = Some(reference);
}
}
macro_rules! declare_level_macro {
(($d:tt), $name:ident, $level:ident) => {
#[macro_export]
macro_rules! $name {
($fmt:literal $d(, $fmt_arg: expr)*) => {
#[cfg(feature = "debug")]
let mut backtrace = $crate::output::backtrace::Backtrace::new();
let i = $crate::output::get_impl();
if i.is_enabled($crate::output::OutputKind::$level) {
let message = format!($fmt $d(, $fmt_arg)*);
#[cfg(feature = "debug")]
let frames = backtrace.frames();
#[cfg(feature = "debug")]
let symbols = frames[0].symbols();
#[cfg(feature = "debug")]
let symbol = &symbols[0];
if let Err(e) = i.log($crate::output::OutputRecord {
kind: $crate::output::OutputKind::$level,
#[cfg(feature = "debug")]
module: String::from(std::module_path!()),
#[cfg(feature = "debug")]
line: symbol.lineno(),
message
}) {
panic!("Could not print: {}", e);
}
}
()
}
}
};
($name:ident, $level:ident) => {
declare_level_macro!(($), $name, $level);
};
}
declare_level_macro!(error, Error);
declare_level_macro!(warning, Warning);
declare_level_macro!(info, Info);
declare_level_macro!(ok, Ok);
declare_level_macro!(debug, Debug);
declare_level_macro!(trace, Trace);