#![allow(dead_code)]
use std::{
io::Write,
os::unix::net::{UnixListener, UnixStream},
sync::Mutex,
time,
};
use anyhow::{anyhow, Context};
use tracing::{error, info};
#[cfg(feature = "test_hooks")]
pub fn emit(event: &str) {
let sock_path = TEST_HOOK_SERVER.sock_path.lock().unwrap();
if sock_path.is_some() {
TEST_HOOK_SERVER.emit_event(event);
}
}
#[cfg(not(feature = "test_hooks"))]
pub fn emit(_event: &str) {
}
#[cfg(feature = "test_hooks")]
pub fn scoped(event: &str) -> ScopedEvent {
ScopedEvent::new(event)
}
#[cfg(not(feature = "test_hooks"))]
pub fn scoped(_event: &str) {}
pub struct ScopedEvent<'a> {
event: &'a str,
}
impl<'a> ScopedEvent<'a> {
pub fn new(event: &'a str) -> Self {
ScopedEvent { event }
}
}
impl std::ops::Drop for ScopedEvent<'_> {
fn drop(&mut self) {
emit(self.event);
}
}
lazy_static::lazy_static! {
pub static ref TEST_HOOK_SERVER: TestHookServer = TestHookServer::new();
}
pub struct TestHookServer {
sock_path: Mutex<Option<String>>,
clients: Mutex<Vec<UnixStream>>,
}
impl TestHookServer {
fn new() -> Self {
TestHookServer { sock_path: Mutex::new(None), clients: Mutex::new(vec![]) }
}
pub fn set_socket_path(&self, path: String) {
let mut sock_path = self.sock_path.lock().unwrap();
*sock_path = Some(path);
}
pub fn wait_for_connect(&self) -> anyhow::Result<()> {
let mut sleep_dur = time::Duration::from_millis(5);
for _ in 0..12 {
{
let clients = self.clients.lock().unwrap();
if !clients.is_empty() {
return Ok(());
}
}
std::thread::sleep(sleep_dur);
sleep_dur *= 2;
}
Err(anyhow!("no connection to test hook server"))
}
pub fn start(&self) {
let sock_path: String;
{
let sock_path_m = self.sock_path.lock().unwrap();
match &*sock_path_m {
Some(s) => {
sock_path = String::from(s);
}
None => {
error!("you must call set_socket_path before calling start");
return;
}
};
}
let listener = match UnixListener::bind(&sock_path).context("binding to socket") {
Ok(l) => l,
Err(e) => {
error!("error binding to test hook socket: {:?}", e);
return;
}
};
info!("listening for test hook connections on {}", &sock_path);
for stream in listener.incoming() {
info!("accepted new test hook client");
let stream = match stream {
Ok(s) => s,
Err(e) => {
error!("error accepting connection to test hook server: {:?}", e);
continue;
}
};
let mut clients = self.clients.lock().unwrap();
clients.push(stream);
}
}
fn emit_event(&self, event: &str) {
info!("emitting event '{}'", event);
let event_line = format!("{event}\n");
let clients = self.clients.lock().unwrap();
for mut client in clients.iter() {
if let Err(e) = client.write_all(event_line.as_bytes()) {
error!("error emitting '{}' event: {:?}", event, e);
}
}
}
}