use std::io::{self, Write};
use std::sync::{Arc, Mutex};
use tempfile::TempDir;
use tracing_subscriber::fmt::MakeWriter;
#[derive(Clone, Default)]
pub(crate) struct CapturedLogs(Arc<Mutex<Vec<u8>>>);
pub(crate) struct CapturedWriter(Arc<Mutex<Vec<u8>>>);
impl Write for CapturedWriter {
fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
let mut bytes = self
.0
.lock()
.map_err(|_| io::Error::other("captured log lock poisoned"))?;
bytes.extend_from_slice(buffer);
Ok(buffer.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'writer> MakeWriter<'writer> for CapturedLogs {
type Writer = CapturedWriter;
fn make_writer(&'writer self) -> Self::Writer {
CapturedWriter(Arc::clone(&self.0))
}
}
impl CapturedLogs {
pub(crate) fn capture<T>(body: impl FnOnce() -> T) -> (Self, T) {
let captured = Self::default();
let subscriber = tracing_subscriber::fmt()
.without_time()
.with_ansi(false)
.with_writer(captured.clone())
.finish();
let value = tracing::subscriber::with_default(subscriber, body);
(captured, value)
}
pub(crate) fn text(&self) -> Result<String, Box<dyn std::error::Error>> {
let bytes = self.0.lock().map_err(|_| "captured log lock poisoned")?;
Ok(String::from_utf8(bytes.clone())?)
}
}
pub(crate) fn make_private(path: &std::path::Path) -> std::io::Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o700))?;
}
#[cfg(not(unix))]
{
let _ = path;
}
Ok(())
}
pub(crate) fn private_tempdir() -> std::io::Result<TempDir> {
let dir = tempfile::tempdir()?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o700))?;
}
Ok(dir)
}