use crate::execute::ExecStep;
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
#[doc(hidden)]
pub trait ActionLogger: Send + Sync {
fn log_step(&self, step: &ExecStep);
fn log_message(&self, level: LogLevel, msg: &str);
}
#[doc(hidden)]
#[derive(Debug, Default, Clone, Copy)]
pub struct TracingLogger;
impl ActionLogger for TracingLogger {
fn log_step(&self, step: &ExecStep) {
tracing::info!(target: "grex::exec", ?step, "action step");
}
fn log_message(&self, level: LogLevel, msg: &str) {
match level {
LogLevel::Trace => tracing::trace!(target: "grex::exec", "{}", msg),
LogLevel::Debug => tracing::debug!(target: "grex::exec", "{}", msg),
LogLevel::Info => tracing::info!(target: "grex::exec", "{}", msg),
LogLevel::Warn => tracing::warn!(target: "grex::exec", "{}", msg),
LogLevel::Error => tracing::error!(target: "grex::exec", "{}", msg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::execute::{ExecResult, ExecStep, PredicateOutcome, StepKind};
use crate::pack::RequireOnFail;
use std::borrow::Cow;
use std::sync::Mutex;
#[derive(Default)]
struct MockLogger {
steps: Mutex<usize>,
messages: Mutex<Vec<(LogLevel, String)>>,
}
impl ActionLogger for MockLogger {
fn log_step(&self, _step: &ExecStep) {
*self.steps.lock().unwrap() += 1;
}
fn log_message(&self, level: LogLevel, msg: &str) {
self.messages.lock().unwrap().push((level, msg.to_owned()));
}
}
fn sample_step() -> ExecStep {
ExecStep {
action_name: Cow::Borrowed("require"),
result: ExecResult::NoOp,
details: StepKind::Require {
outcome: PredicateOutcome::Unsatisfied,
on_fail: RequireOnFail::Skip,
},
}
}
#[test]
fn mock_logger_counts_step_calls() {
let m = MockLogger::default();
let s = sample_step();
m.log_step(&s);
m.log_step(&s);
assert_eq!(*m.steps.lock().unwrap(), 2);
assert!(m.messages.lock().unwrap().is_empty());
}
#[test]
fn mock_logger_records_messages() {
let m = MockLogger::default();
m.log_message(LogLevel::Info, "hello");
m.log_message(LogLevel::Error, "boom");
let msgs = m.messages.lock().unwrap();
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0], (LogLevel::Info, "hello".to_owned()));
assert_eq!(msgs[1], (LogLevel::Error, "boom".to_owned()));
}
#[test]
fn tracing_logger_does_not_panic() {
let t = TracingLogger;
t.log_step(&sample_step());
t.log_message(LogLevel::Trace, "t");
t.log_message(LogLevel::Debug, "d");
t.log_message(LogLevel::Info, "i");
t.log_message(LogLevel::Warn, "w");
t.log_message(LogLevel::Error, "e");
}
#[test]
fn trait_is_object_safe() {
let _: &dyn ActionLogger = &TracingLogger;
}
}