Skip to main content

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}