1#[cfg(feature = "debug")]
2pub use backtrace;
3use crate::error::Error;
4
5#[derive(PartialEq, Eq)]
6pub enum OutputKind {
7 Ok,
8 Error,
9 Warning,
10 Info,
11 Debug,
12 Trace
13}
14pub struct OutputRecord {
15 pub kind: OutputKind,
16 #[cfg(feature = "debug")]
17 pub module: String,
18 #[cfg(feature = "debug")]
19 pub line: Option<u32>,
20 pub message: String
21}
22
23pub trait CliOutput: Sync {
24 fn is_enabled(&self, kind: OutputKind) -> bool;
25 fn log(&self, record: OutputRecord) -> Result<(), Error>;
26}
27
28static mut IMPL: Option<&dyn CliOutput> = None;
29
30const MISSING_IMPL_MESSAGE: &str = "Log implementation is not set";
31
32pub fn get_impl() -> &'static dyn CliOutput {
33 unsafe {
34 return IMPL.expect(MISSING_IMPL_MESSAGE);
35 }
36}
37
38pub fn set_impl(reference: &'static dyn CliOutput) {
39 unsafe {
40 if let Some(_) = IMPL {
41 panic!("Cannot set log implementation twice"); }
43 IMPL = Some(reference);
44 }
45}
46
47macro_rules! declare_level_macro {
48 (($d:tt), $name:ident, $level:ident) => {
49 #[macro_export]
50 macro_rules! $name {
51 ($fmt:literal $d(, $fmt_arg: expr)*) => {
52 #[cfg(feature = "debug")]
53 let mut backtrace = $crate::output::backtrace::Backtrace::new();
54 let i = $crate::output::get_impl();
55
56 if i.is_enabled($crate::output::OutputKind::$level) {
57 let message = format!($fmt $d(, $fmt_arg)*);
58 #[cfg(feature = "debug")]
59 let frames = backtrace.frames();
60 #[cfg(feature = "debug")]
61 let symbols = frames[0].symbols();
62 #[cfg(feature = "debug")]
63 let symbol = &symbols[0];
64 if let Err(e) = i.log($crate::output::OutputRecord {
65 kind: $crate::output::OutputKind::$level,
66 #[cfg(feature = "debug")]
67 module: String::from(std::module_path!()),
68 #[cfg(feature = "debug")]
69 line: symbol.lineno(),
70 message
71 }) {
72 panic!("Could not print: {}", e);
73 }
74 }
75 ()
76 }
77 }
78 };
79 ($name:ident, $level:ident) => {
80 declare_level_macro!(($), $name, $level);
81 };
82}
83
84declare_level_macro!(error, Error);
85declare_level_macro!(warning, Warning);
86declare_level_macro!(info, Info);
87declare_level_macro!(ok, Ok);
88declare_level_macro!(debug, Debug);
89declare_level_macro!(trace, Trace);