1use std::any::Any;
7use std::fmt;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct EventType(String);
12
13impl EventType {
14 #[must_use]
16 pub fn new(name: impl Into<String>) -> Self {
17 Self(name.into())
18 }
19
20 #[must_use]
22 pub fn as_str(&self) -> &str {
23 &self.0
24 }
25}
26
27impl fmt::Display for EventType {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.write_str(&self.0)
30 }
31}
32
33pub trait Event: Any + Send + Sync + 'static {
39 fn event_type(&self) -> EventType;
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct HookError {
46 message: String,
47 fatal: bool,
48}
49
50impl HookError {
51 #[must_use]
53 pub fn new(message: impl Into<String>) -> Self {
54 Self {
55 message: message.into(),
56 fatal: false,
57 }
58 }
59
60 #[must_use]
62 pub fn fatal(message: impl Into<String>) -> Self {
63 Self {
64 message: message.into(),
65 fatal: true,
66 }
67 }
68
69 #[must_use]
71 pub const fn is_fatal(&self) -> bool {
72 self.fatal
73 }
74
75 #[must_use]
77 pub fn message(&self) -> &str {
78 &self.message
79 }
80}
81
82impl fmt::Display for HookError {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 f.write_str(&self.message)
85 }
86}
87
88impl std::error::Error for HookError {}
89
90pub type HookResult = Result<(), HookError>;
92
93#[cfg(test)]
94mod tests {
95 use super::{Event, EventType, HookError, HookResult};
96
97 struct Ping {
98 count: u32,
99 }
100
101 impl Event for Ping {
102 fn event_type(&self) -> EventType {
103 EventType::new("ping")
104 }
105 }
106
107 #[test]
108 fn event_type_equality() {
109 assert_eq!(EventType::new("ping"), EventType::new("ping"));
110 assert_ne!(EventType::new("ping"), EventType::new("pong"));
111 }
112
113 #[test]
114 fn event_type_is_static_metadata() {
115 let ping = Ping { count: 42 };
116 assert_eq!(ping.event_type(), EventType::new("ping"));
117 assert_eq!(ping.count, 42);
118 }
119
120 #[test]
121 fn hook_result_success() {
122 let result: HookResult = Ok(());
123 assert!(result.is_ok());
124 }
125
126 #[test]
127 fn hook_error_fatal_flag() {
128 let err = HookError::fatal("budget exceeded");
129 assert!(err.is_fatal());
130 assert_eq!(err.message(), "budget exceeded");
131 }
132}