Skip to main content

oxihuman_core/
message_router.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Message routing table — maps message types to handler identifiers.
6
7/// A routing rule entry.
8#[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/// A message to be routed.
17#[derive(Clone, Debug)]
18pub struct RoutableMessage {
19    pub message_type: String,
20    pub payload: String,
21    pub source: String,
22}
23
24/// Routing configuration.
25#[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
40/// The message router that maps types to handlers.
41pub struct MessageRouter {
42    pub config: MessageRouterConfig,
43    routes: Vec<RouteEntry>,
44}
45
46/// Creates a new message router.
47pub fn new_router(config: MessageRouterConfig) -> MessageRouter {
48    MessageRouter {
49        config,
50        routes: Vec::new(),
51    }
52}
53
54/// Adds a route, returning false if the table is full.
55pub 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
63/// Removes all routes for a given message type.
64pub 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
70/// Routes a message, returning the handler ID with the highest priority.
71pub 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
88/// Enables or disables all routes for a given handler.
89pub 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
99/// Returns all handler IDs registered in the router.
100pub 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    /// Creates a new router with default config.
109    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}