#![allow(missing_docs)]
pub use anyhow::{anyhow, Error};
use super::Logger;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
lazy_static! {
static ref GLOBAL_MUTEX: Mutex<()> = Mutex::new(());
static ref LOGGER: Arc<Mutex<Option<Logger>>> = Arc::new(Mutex::new(None));
static ref LAST_MESSAGE: Mutex<(String, String)> = Mutex::new(("".to_owned(), "".to_owned()));
}
#[derive(Clone, Debug)]
pub struct Endpoint {
name: String,
}
impl Endpoint {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl TryFrom<&str> for Endpoint {
type Error = Error;
fn try_from(name: &str) -> Result<Self, Self::Error> {
Ok(Self {
name: name.to_owned(),
})
}
}
impl TryFrom<String> for Endpoint {
type Error = Error;
fn try_from(name: String) -> Result<Self, Self::Error> {
Ok(Self { name })
}
}
impl std::io::Write for Endpoint {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let msg = String::from_utf8_lossy(buf).to_string();
*LAST_MESSAGE.lock().unwrap() = (self.name.clone(), msg);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub struct LoggerGuard {
_guard: MutexGuard<'static, ()>,
}
impl LoggerGuard {
pub fn assert_contains(&self, endpoint: &str, needle: &str) {
let last = LAST_MESSAGE.lock().unwrap();
let (last_endpoint, last_msg) = (last.0.to_owned(), last.1.to_owned());
drop(last);
assert!(
endpoint == last_endpoint.as_str() && last_msg.contains(needle),
"looking for {:?}: {:?}; found {:?}: {:?}",
endpoint,
needle,
last_endpoint,
last_msg
);
}
pub fn assert_absent(&self, endpoint: &str, poison: &str) {
let last = LAST_MESSAGE.lock().unwrap();
let (last_endpoint, last_msg) = (last.0.to_owned(), last.1.to_owned());
drop(last);
assert!(
endpoint != last_endpoint.as_str() || !last_msg.contains(poison),
"avoiding {:?}: {:?}; found {:?}: {:?}",
endpoint,
poison,
last_endpoint,
last_msg
);
}
}
impl Drop for LoggerGuard {
fn drop(&mut self) {
*LOGGER.lock().unwrap_or_else(PoisonError::into_inner) = None;
}
}
pub fn reset_logger(logger: Logger) -> LoggerGuard {
let guard = GLOBAL_MUTEX.lock().unwrap_or_else(PoisonError::into_inner);
*LAST_MESSAGE.lock().unwrap() = ("".to_owned(), "".to_owned());
log::set_max_level(logger.max_level());
*LOGGER.lock().unwrap() = Some(logger);
let _ = log::set_logger(&GlobalLogger);
LoggerGuard { _guard: guard }
}
struct GlobalLogger;
impl log::Log for GlobalLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
if let Some(logger) = LOGGER.lock().unwrap().as_ref() {
logger.enabled(metadata)
} else {
false
}
}
fn log(&self, record: &log::Record) {
if let Some(logger) = LOGGER.lock().unwrap().as_ref() {
logger.log(record)
}
}
fn flush(&self) {}
}