A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.2 KiB

use crate::resources::Resource;
use crate::to_artifact::ToArtifact;
use slog::Drain;
use slog::{OwnedKVList, Record};
use slog_async::AsyncRecord;
use std::cell::RefCell;
use std::error::Error;
use std::fmt::Debug;
use std::io::{self, Write};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub trait AddableResource: 'static + Resource + Debug {}
impl<R> AddableResource for R where R: 'static + Resource + Debug {}
pub type AddResult<R> = Result<(<R as ToArtifact>::Artifact, bool), Box<dyn Error>>;
pub type InternalAddResult<R> = Result<(<R as ToArtifact>::Artifact, bool), Box<dyn Error>>;
// From https://users.rust-lang.org/t/how-to-send-a-writer-into-a-thread/4965/10
#[derive(Clone)]
struct Output<W>(Rc<RefCell<W>>);
impl<W: Write> Output<W> {
pub fn new(w: W) -> Self {
Self(Rc::new(RefCell::new(w)))
}
}
impl<W: Write> Write for Output<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.borrow_mut().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.borrow_mut().flush()
}
}
#[derive(Clone, Default)]
pub struct Recorder(Arc<Mutex<Vec<AsyncRecord>>>);
impl Drain for Recorder {
type Ok = ();
type Err = slog::Never;
fn log(&self, record: &Record<'_>, logger_values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
self
.0
.lock()
.unwrap()
.push(AsyncRecord::from(record, logger_values));
Ok(())
}
}
impl Recorder {
pub fn to_string(&self, filter_level: slog::Level) -> String {
let output = Output::new(vec![]);
{
let decorator = slog_term::PlainDecorator::new(output.clone());
let drain = slog_term::CompactFormat::new(decorator).build();
for record in &*self.0.lock().unwrap() {
record.as_record_values(|record, kv| {
if record.level() <= filter_level {
drain.log(record, kv).unwrap();
}
});
}
}
String::from_utf8(Rc::try_unwrap(output.0).unwrap().into_inner())
.expect("Record output should be valid UTF-8")
}
}
#[cfg(test)]
mod test {
use super::Recorder;
use slog::Level;
#[test]
fn records_no_output() {
let recorder = Recorder::default();
assert_eq!(recorder.to_string(Level::Trace), "");
}
}