rivet_logger/handlers/
stack.rs1use 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}