use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
use crate::host::handler;
static LOGGER: HostLogger = HostLogger;
static LVL: [i32; 6] = [3, 2, 1, 0, -1, -1];
fn map_to_host(level: Level) -> i32 {
LVL[level as usize]
}
struct HostLogger;
impl Log for HostLogger {
#[inline]
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
handler::log(map_to_host(record.metadata().level()), format!("{}", record.args()).as_bytes());
}
fn flush(&self) {}
}
#[inline]
pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> {
log::set_max_level(max_level(level.to_level_filter()));
log::set_logger(&LOGGER)?;
Ok(())
}
fn max_level(level: LevelFilter) -> LevelFilter {
if handler::log_enabled(level.to_level().map_or_else(|| 3, map_to_host)) { level } else { level.decrement_severity() }
}
#[inline]
pub fn init() -> Result<(), SetLoggerError> {
init_with_level(Level::Info)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init_log_with_level() {
let _result = init_with_level(Level::Info);
}
#[test]
fn map_to_host_error() {
assert_eq!(map_to_host(Level::Error), 2);
}
#[test]
fn map_to_host_warn() {
assert_eq!(map_to_host(Level::Warn), 1);
}
#[test]
fn map_to_host_info() {
assert_eq!(map_to_host(Level::Info), 0);
}
#[test]
fn map_to_host_debug() {
assert_eq!(map_to_host(Level::Debug), -1);
}
#[test]
fn map_to_host_trace() {
assert_eq!(map_to_host(Level::Trace), -1);
}
#[test]
fn host_logger_enabled_within_max_level() {
log::set_max_level(LevelFilter::Info);
let metadata = log::Metadata::builder().level(Level::Info).target("test").build();
assert!(LOGGER.enabled(&metadata));
}
#[test]
fn host_logger_enabled_below_max_level() {
log::set_max_level(LevelFilter::Info);
let metadata = log::Metadata::builder().level(Level::Error).target("test").build();
assert!(LOGGER.enabled(&metadata));
}
#[test]
fn host_logger_disabled_above_max_level() {
log::set_max_level(LevelFilter::Warn);
let metadata = log::Metadata::builder().level(Level::Debug).target("test").build();
assert!(!LOGGER.enabled(&metadata));
}
#[test]
fn host_logger_log_enabled_message() {
log::set_max_level(LevelFilter::Info);
log::info!("test message");
}
#[test]
fn host_logger_log_disabled_message() {
log::set_max_level(LevelFilter::Error);
log::debug!("this should be filtered");
}
#[test]
fn host_logger_flush() {
LOGGER.flush();
}
#[test]
fn log_enabled_check() {
assert!(handler::log_enabled(0)); assert!(handler::log_enabled(1)); assert!(handler::log_enabled(2)); assert!(handler::log_enabled(3)); assert!(!handler::log_enabled(-1)); assert!(!handler::log_enabled(4)); }
#[test]
fn handler_log_call() {
handler::log(2, b"test log message");
}
#[test]
fn test_init_default_level() {
let _result = init();
}
#[test]
fn test_max_level_enabled() {
let level = max_level(LevelFilter::Info);
assert_eq!(level, LevelFilter::Info);
}
#[test]
fn test_max_level_disabled_decrements() {
let level = max_level(LevelFilter::Trace);
assert!(level < LevelFilter::Trace);
}
#[test]
fn host_logger_log_direct_call() {
log::set_max_level(LevelFilter::Info);
let record = log::Record::builder().level(Level::Info).target("test").args(format_args!("direct log test")).build();
LOGGER.log(&record);
}
#[test]
fn host_logger_log_skips_disabled_level() {
log::set_max_level(LevelFilter::Error);
let record =
log::Record::builder().level(Level::Debug).target("test").args(format_args!("this should be skipped")).build();
LOGGER.log(&record);
}
}