mod_events/result.rs
1//! Event dispatch result types.
2
3use crate::ListenerError;
4
5/// Outcome of an event dispatch.
6///
7/// Carries the per-listener error list and aggregate counts. The
8/// internal representation is *lazy*: a `DispatchResult` for a fully
9/// successful dispatch holds an empty `Vec` (zero heap allocation
10/// — `Vec::new` does not allocate until first `push`). Allocation
11/// only happens when at least one listener returns `Err` or panics,
12/// keeping the success path allocation-free.
13#[derive(Debug)]
14#[must_use = "DispatchResult carries listener errors that will be silently dropped if ignored"]
15pub struct DispatchResult {
16 /// Number of listeners that ran. Includes both successes and
17 /// failures. Equal to `success_count + error_count` on the
18 /// non-blocked path; equal to `0` if `blocked == true`.
19 listener_count: usize,
20 /// Errors produced by failing listeners, in dispatch order.
21 /// Stays empty (and unallocated) when every listener succeeds.
22 errors: Vec<ListenerError>,
23 /// `true` iff the dispatch was halted by middleware before any
24 /// listener ran.
25 blocked: bool,
26}
27
28impl DispatchResult {
29 /// Construct a result for a normal (non-blocked) dispatch.
30 pub(crate) fn new(listener_count: usize, errors: Vec<ListenerError>) -> Self {
31 Self {
32 listener_count,
33 errors,
34 blocked: false,
35 }
36 }
37
38 /// Construct a result for a dispatch that was halted by middleware.
39 pub(crate) fn blocked() -> Self {
40 Self {
41 listener_count: 0,
42 errors: Vec::new(),
43 blocked: false,
44 }
45 .with_blocked()
46 }
47
48 fn with_blocked(mut self) -> Self {
49 self.blocked = true;
50 self
51 }
52
53 /// Whether the dispatch was halted by middleware.
54 #[must_use]
55 pub fn is_blocked(&self) -> bool {
56 self.blocked
57 }
58
59 /// Total number of listeners invoked (successes + failures).
60 /// Returns `0` when the dispatch was blocked.
61 #[must_use]
62 pub fn listener_count(&self) -> usize {
63 self.listener_count
64 }
65
66 /// Number of listeners that returned `Ok(())` (or completed
67 /// without panicking).
68 #[must_use]
69 pub fn success_count(&self) -> usize {
70 self.listener_count.saturating_sub(self.errors.len())
71 }
72
73 /// Number of listeners that returned `Err(_)` or panicked. A
74 /// panicking listener contributes one error with the message
75 /// prefix `"listener panicked: "`.
76 #[must_use]
77 pub fn error_count(&self) -> usize {
78 self.errors.len()
79 }
80
81 /// Borrow every error produced by failing listeners, in dispatch order.
82 #[must_use]
83 pub fn errors(&self) -> &[ListenerError] {
84 &self.errors
85 }
86
87 /// `true` iff the dispatch was not blocked and every listener
88 /// returned `Ok(())`.
89 #[must_use]
90 pub fn all_succeeded(&self) -> bool {
91 !self.blocked && self.errors.is_empty()
92 }
93
94 /// `true` iff at least one listener returned `Err(_)` or panicked.
95 #[must_use]
96 pub fn has_errors(&self) -> bool {
97 !self.errors.is_empty()
98 }
99}