use crate::_doc::{man7, manbsd};
use std::fmt;
pub trait Logger {
#[doc = man7!(3 pam_syslog)]
#[doc = manbsd!(3 openpam_log)]
fn log(&self, level: Level, loc: Location<'_>, entry: fmt::Arguments);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Level {
Error,
Warn,
Info,
Debug,
}
#[derive(Clone, Copy, Debug, Default)]
#[non_exhaustive]
pub struct Location<'a> {
pub file: &'a str,
pub line: u32,
pub function: &'a str,
}
impl<'a> Location<'a> {
pub fn new(file: &'a str, line: u32, function: &'a str) -> Self {
Self {
file,
line,
function,
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! location {
() => {
$crate::logging::Location::new(file!(), line!(), $crate::__function!())
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __log_internal {
($handle:expr, $level:ident, $($arg:tt)+) => {
$handle.log($crate::logging::Level::$level, $crate::location!(), format_args!($($arg)+));
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __function {
() => {{
fn p() {}
fn f<T>(_: T) -> &'static str {
::std::any::type_name::<T>()
}
f(p).trim_end_matches("::p")
}};
}
#[macro_export]
macro_rules! error { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Error, $($arg)+);}}
#[macro_export]
macro_rules! warn { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Warn, $($arg)+);}}
#[macro_export]
macro_rules! info { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Info, $($arg)+);}}
#[macro_export]
macro_rules! debug { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Debug, $($arg)+);}}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::fmt;
#[test]
fn test_logging() {
struct TestLog(RefCell<Vec<(Level, String)>>);
impl Logger for TestLog {
fn log(&self, level: Level, _: Location<'_>, text: fmt::Arguments) {
self.0.borrow_mut().push((level, text.to_string()))
}
}
let logger = TestLog(Default::default());
let something = Level::Error;
error!(logger, "here is another thing: {}", 99);
warn!(logger, "watch out!");
info!(logger, "here is some info: {info}", info = "information");
debug!(logger, "here is something: {something:?}");
let logged = logger.0.into_inner();
assert_eq!(
vec![
(Level::Error, "here is another thing: 99".to_owned()),
(Level::Warn, "watch out!".to_owned()),
(Level::Info, "here is some info: information".to_owned()),
(Level::Debug, "here is something: Error".to_owned()),
],
logged
);
}
}