Skip to main content

rivet_logger/handlers/
stack.rs

1use std::io;
2use std::sync::Arc;
3
4use super::Handler;
5
6pub struct Stack {
7    children: Vec<Arc<dyn Handler>>,
8}
9
10impl Stack {
11    pub fn new(children: Vec<Arc<dyn Handler>>) -> Self {
12        Self { children }
13    }
14
15    pub fn push(&mut self, child: Arc<dyn Handler>) {
16        self.children.push(child);
17    }
18
19    pub fn children(&self) -> &[Arc<dyn Handler>] {
20        &self.children
21    }
22}
23
24impl Handler for Stack {
25    fn log(&self, message: &str) -> io::Result<()> {
26        let mut first_err: Option<io::Error> = None;
27        let mut has_success = false;
28
29        for child in &self.children {
30            match child.log(message) {
31                Ok(()) => has_success = true,
32                Err(err) if first_err.is_none() => first_err = Some(err),
33                Err(_) => {}
34            }
35        }
36
37        if has_success {
38            return Ok(());
39        }
40
41        Err(first_err.unwrap_or_else(|| io::Error::other("stack handler has no child handlers")))
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use std::io;
48    use std::sync::{Arc, Mutex};
49
50    use super::{Handler, Stack};
51
52    struct Recording {
53        id: &'static str,
54        calls: Arc<Mutex<Vec<&'static str>>>,
55        should_fail: bool,
56    }
57
58    impl Recording {
59        fn new(id: &'static str, calls: Arc<Mutex<Vec<&'static str>>>, should_fail: bool) -> Self {
60            Self {
61                id,
62                calls,
63                should_fail,
64            }
65        }
66    }
67
68    impl Handler for Recording {
69        fn log(&self, _message: &str) -> io::Result<()> {
70            self.calls
71                .lock()
72                .expect("calls lock poisoned")
73                .push(self.id);
74            if self.should_fail {
75                return Err(io::Error::other(format!("handler {} failed", self.id)));
76            }
77            Ok(())
78        }
79    }
80
81    #[test]
82    fn fans_out_to_all_children_in_order() {
83        let calls = Arc::new(Mutex::new(Vec::new()));
84        let first = Arc::new(Recording::new("first", Arc::clone(&calls), false));
85        let second = Arc::new(Recording::new("second", Arc::clone(&calls), false));
86
87        let stack = Stack::new(vec![first, second]);
88        stack.log("hello").expect("stack should write");
89
90        let got = calls.lock().expect("calls lock poisoned").clone();
91        assert_eq!(got, vec!["first", "second"]);
92    }
93
94    #[test]
95    fn succeeds_when_at_least_one_child_succeeds() {
96        let calls = Arc::new(Mutex::new(Vec::new()));
97        let fail = Arc::new(Recording::new("fail", Arc::clone(&calls), true));
98        let ok = Arc::new(Recording::new("ok", Arc::clone(&calls), false));
99
100        let stack = Stack::new(vec![fail, ok]);
101        stack
102            .log("hello")
103            .expect("stack should succeed when one child succeeds");
104    }
105
106    #[test]
107    fn fails_when_all_children_fail() {
108        let calls = Arc::new(Mutex::new(Vec::new()));
109        let first = Arc::new(Recording::new("first", Arc::clone(&calls), true));
110        let second = Arc::new(Recording::new("second", Arc::clone(&calls), true));
111
112        let stack = Stack::new(vec![first, second]);
113        let err = stack.log("hello").expect_err("stack should fail");
114        assert!(err.to_string().contains("handler first failed"));
115    }
116}