1#![allow(dead_code)]
4
5#[derive(Clone, Debug)]
9pub struct RouteEntry {
10 pub message_type: String,
11 pub handler_id: String,
12 pub priority: i32,
13 pub enabled: bool,
14}
15
16#[derive(Clone, Debug)]
18pub struct RoutableMessage {
19 pub message_type: String,
20 pub payload: String,
21 pub source: String,
22}
23
24#[derive(Clone, Debug)]
26pub struct MessageRouterConfig {
27 pub default_handler: Option<String>,
28 pub max_routes: usize,
29}
30
31impl Default for MessageRouterConfig {
32 fn default() -> Self {
33 Self {
34 default_handler: None,
35 max_routes: 256,
36 }
37 }
38}
39
40pub struct MessageRouter {
42 pub config: MessageRouterConfig,
43 routes: Vec<RouteEntry>,
44}
45
46pub fn new_router(config: MessageRouterConfig) -> MessageRouter {
48 MessageRouter {
49 config,
50 routes: Vec::new(),
51 }
52}
53
54pub fn add_route(router: &mut MessageRouter, entry: RouteEntry) -> bool {
56 if router.routes.len() >= router.config.max_routes {
57 return false;
58 }
59 router.routes.push(entry);
60 true
61}
62
63pub fn remove_routes_for(router: &mut MessageRouter, message_type: &str) -> usize {
65 let before = router.routes.len();
66 router.routes.retain(|r| r.message_type != message_type);
67 before.saturating_sub(router.routes.len())
68}
69
70pub fn route_message<'a>(router: &'a MessageRouter, msg: &RoutableMessage) -> Option<&'a str> {
72 let mut best: Option<&RouteEntry> = None;
73 for entry in router
74 .routes
75 .iter()
76 .filter(|e| e.enabled && e.message_type == msg.message_type)
77 {
78 match best {
79 None => best = Some(entry),
80 Some(b) if entry.priority > b.priority => best = Some(entry),
81 _ => {}
82 }
83 }
84 best.map(|e| e.handler_id.as_str())
85 .or(router.config.default_handler.as_deref())
86}
87
88pub fn set_handler_enabled(router: &mut MessageRouter, handler_id: &str, enabled: bool) {
90 for r in router
91 .routes
92 .iter_mut()
93 .filter(|r| r.handler_id == handler_id)
94 {
95 r.enabled = enabled;
96 }
97}
98
99pub fn all_handler_ids(router: &MessageRouter) -> Vec<String> {
101 let mut ids: Vec<String> = router.routes.iter().map(|r| r.handler_id.clone()).collect();
102 ids.sort();
103 ids.dedup();
104 ids
105}
106
107impl MessageRouter {
108 pub fn new(config: MessageRouterConfig) -> Self {
110 new_router(config)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 fn make_router() -> MessageRouter {
119 new_router(MessageRouterConfig::default())
120 }
121
122 fn entry(msg_type: &str, handler: &str, priority: i32) -> RouteEntry {
123 RouteEntry {
124 message_type: msg_type.into(),
125 handler_id: handler.into(),
126 priority,
127 enabled: true,
128 }
129 }
130
131 fn msg(msg_type: &str) -> RoutableMessage {
132 RoutableMessage {
133 message_type: msg_type.into(),
134 payload: "{}".into(),
135 source: "test".into(),
136 }
137 }
138
139 #[test]
140 fn test_add_and_route() {
141 let mut r = make_router();
142 add_route(&mut r, entry("click", "ui_handler", 10));
143 assert_eq!(route_message(&r, &msg("click")), Some("ui_handler"));
144 }
145
146 #[test]
147 fn test_higher_priority_wins() {
148 let mut r = make_router();
149 add_route(&mut r, entry("ev", "low_handler", 1));
150 add_route(&mut r, entry("ev", "high_handler", 10));
151 assert_eq!(route_message(&r, &msg("ev")), Some("high_handler"));
152 }
153
154 #[test]
155 fn test_unknown_type_uses_default_handler() {
156 let mut r = new_router(MessageRouterConfig {
157 default_handler: Some("fallback".into()),
158 max_routes: 64,
159 });
160 add_route(&mut r, entry("known", "h", 1));
161 assert_eq!(route_message(&r, &msg("unknown")), Some("fallback"));
162 }
163
164 #[test]
165 fn test_unknown_type_without_default_returns_none() {
166 let r = make_router();
167 assert!(route_message(&r, &msg("notype")).is_none());
168 }
169
170 #[test]
171 fn test_remove_routes_for() {
172 let mut r = make_router();
173 add_route(&mut r, entry("t1", "h1", 1));
174 add_route(&mut r, entry("t1", "h2", 2));
175 add_route(&mut r, entry("t2", "h3", 1));
176 let removed = remove_routes_for(&mut r, "t1");
177 assert_eq!(removed, 2);
178 assert!(route_message(&r, &msg("t1")).is_none());
179 }
180
181 #[test]
182 fn test_disabled_route_skipped() {
183 let mut r = make_router();
184 add_route(&mut r, entry("ping", "handler_a", 5));
185 set_handler_enabled(&mut r, "handler_a", false);
186 assert!(route_message(&r, &msg("ping")).is_none());
187 }
188
189 #[test]
190 fn test_all_handler_ids_unique() {
191 let mut r = make_router();
192 add_route(&mut r, entry("a", "h1", 1));
193 add_route(&mut r, entry("b", "h1", 1));
194 add_route(&mut r, entry("c", "h2", 1));
195 let ids = all_handler_ids(&r);
196 assert_eq!(ids.len(), 2);
197 }
198
199 #[test]
200 fn test_capacity_limit() {
201 let mut r = new_router(MessageRouterConfig {
202 default_handler: None,
203 max_routes: 2,
204 });
205 add_route(&mut r, entry("a", "h", 1));
206 add_route(&mut r, entry("b", "h", 1));
207 let ok = add_route(&mut r, entry("c", "h", 1));
208 assert!(!ok);
209 }
210
211 #[test]
212 fn test_enable_handler_after_disable() {
213 let mut r = make_router();
214 add_route(&mut r, entry("e", "he", 5));
215 set_handler_enabled(&mut r, "he", false);
216 set_handler_enabled(&mut r, "he", true);
217 assert_eq!(route_message(&r, &msg("e")), Some("he"));
218 }
219}