use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolOutputStream {
Stdout,
Stderr,
}
impl ToolOutputStream {
pub fn as_str(self) -> &'static str {
match self {
Self::Stdout => "stdout",
Self::Stderr => "stderr",
}
}
}
type OutputFn = dyn Fn(ToolOutputStream, &str) + Send + Sync;
#[derive(Clone, Default)]
pub struct ToolOutputNotifier(Option<Arc<OutputFn>>);
impl ToolOutputNotifier {
pub fn new(f: impl Fn(ToolOutputStream, &str) + Send + Sync + 'static) -> Self {
Self(Some(Arc::new(f)))
}
pub fn is_set(&self) -> bool {
self.0.is_some()
}
pub fn notify(&self, stream: ToolOutputStream, text: &str) {
if let Some(f) = &self.0 {
f(stream, text);
}
}
}
impl fmt::Debug for ToolOutputNotifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.0.is_some() {
"ToolOutputNotifier(set)"
} else {
"ToolOutputNotifier(none)"
})
}
}
impl PartialEq for ToolOutputNotifier {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(None, None) => true,
(Some(a), Some(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl Eq for ToolOutputNotifier {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
#[test]
fn notifier_delivers_and_default_is_silent() {
ToolOutputNotifier::default().notify(ToolOutputStream::Stdout, "ignored");
let seen = Arc::new(Mutex::new(Vec::new()));
let sink = seen.clone();
let notifier = ToolOutputNotifier::new(move |stream, text| {
sink.lock()
.unwrap()
.push((stream.as_str(), text.to_string()));
});
notifier.notify(ToolOutputStream::Stderr, "warn");
assert!(notifier.is_set());
assert_eq!(*seen.lock().unwrap(), vec![("stderr", "warn".to_string())]);
}
}