use std::io;
use std::sync::Arc;
use super::Handler;
pub struct Stack {
children: Vec<Arc<dyn Handler>>,
}
impl Stack {
pub fn new(children: Vec<Arc<dyn Handler>>) -> Self {
Self { children }
}
pub fn push(&mut self, child: Arc<dyn Handler>) {
self.children.push(child);
}
pub fn children(&self) -> &[Arc<dyn Handler>] {
&self.children
}
}
impl Handler for Stack {
fn log(&self, message: &str) -> io::Result<()> {
let mut first_err: Option<io::Error> = None;
let mut has_success = false;
for child in &self.children {
match child.log(message) {
Ok(()) => has_success = true,
Err(err) if first_err.is_none() => first_err = Some(err),
Err(_) => {}
}
}
if has_success {
return Ok(());
}
Err(first_err.unwrap_or_else(|| io::Error::other("stack handler has no child handlers")))
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::sync::{Arc, Mutex};
use super::{Handler, Stack};
struct Recording {
id: &'static str,
calls: Arc<Mutex<Vec<&'static str>>>,
should_fail: bool,
}
impl Recording {
fn new(id: &'static str, calls: Arc<Mutex<Vec<&'static str>>>, should_fail: bool) -> Self {
Self {
id,
calls,
should_fail,
}
}
}
impl Handler for Recording {
fn log(&self, _message: &str) -> io::Result<()> {
self.calls
.lock()
.expect("calls lock poisoned")
.push(self.id);
if self.should_fail {
return Err(io::Error::other(format!("handler {} failed", self.id)));
}
Ok(())
}
}
#[test]
fn fans_out_to_all_children_in_order() {
let calls = Arc::new(Mutex::new(Vec::new()));
let first = Arc::new(Recording::new("first", Arc::clone(&calls), false));
let second = Arc::new(Recording::new("second", Arc::clone(&calls), false));
let stack = Stack::new(vec![first, second]);
stack.log("hello").expect("stack should write");
let got = calls.lock().expect("calls lock poisoned").clone();
assert_eq!(got, vec!["first", "second"]);
}
#[test]
fn succeeds_when_at_least_one_child_succeeds() {
let calls = Arc::new(Mutex::new(Vec::new()));
let fail = Arc::new(Recording::new("fail", Arc::clone(&calls), true));
let ok = Arc::new(Recording::new("ok", Arc::clone(&calls), false));
let stack = Stack::new(vec![fail, ok]);
stack
.log("hello")
.expect("stack should succeed when one child succeeds");
}
#[test]
fn fails_when_all_children_fail() {
let calls = Arc::new(Mutex::new(Vec::new()));
let first = Arc::new(Recording::new("first", Arc::clone(&calls), true));
let second = Arc::new(Recording::new("second", Arc::clone(&calls), true));
let stack = Stack::new(vec![first, second]);
let err = stack.log("hello").expect_err("stack should fail");
assert!(err.to_string().contains("handler first failed"));
}
}