Skip to main content

hypen_server/
context.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3
4use serde_json::Value;
5
6use crate::events::EventEmitter;
7
8/// Global context providing cross-module communication.
9///
10/// Shared across all module instances in an application. Provides:
11/// - Module registry (look up other modules' state)
12/// - Global event emitter
13/// - Router access
14///
15/// # Example
16///
17/// ```rust
18/// use hypen_server::context::GlobalContext;
19///
20/// let ctx = GlobalContext::new();
21///
22/// // Register module state
23/// ctx.register_module_state("counter", serde_json::json!({"count": 0}));
24///
25/// // Access from another module
26/// let state = ctx.get_module_state("counter");
27/// assert!(state.is_some());
28/// ```
29pub struct GlobalContext {
30    /// Module states indexed by name.
31    module_states: Mutex<HashMap<String, Value>>,
32
33    /// Global event emitter.
34    events: EventEmitter,
35
36    /// Router (set externally when routing is configured).
37    router: Mutex<Option<Arc<crate::router::HypenRouter>>>,
38}
39
40impl GlobalContext {
41    pub fn new() -> Self {
42        Self {
43            module_states: Mutex::new(HashMap::new()),
44            events: EventEmitter::new(),
45            router: Mutex::new(None),
46        }
47    }
48
49    // ---- Module State Registry ----
50
51    /// Register (or update) a module's state in the global context.
52    pub fn register_module_state(&self, name: impl Into<String>, state: Value) {
53        self.module_states
54            .lock()
55            .unwrap()
56            .insert(name.into(), state);
57    }
58
59    /// Get a snapshot of a module's state by name.
60    pub fn get_module_state(&self, name: &str) -> Option<Value> {
61        self.module_states.lock().unwrap().get(name).cloned()
62    }
63
64    /// Check if a module is registered.
65    pub fn has_module(&self, name: &str) -> bool {
66        self.module_states.lock().unwrap().contains_key(name)
67    }
68
69    /// Get all registered module names.
70    pub fn module_names(&self) -> Vec<String> {
71        self.module_states.lock().unwrap().keys().cloned().collect()
72    }
73
74    /// Remove a module from the context.
75    pub fn unregister_module(&self, name: &str) {
76        self.module_states.lock().unwrap().remove(name);
77    }
78
79    /// Get a merged view of all module states.
80    pub fn global_state(&self) -> Value {
81        let states = self.module_states.lock().unwrap();
82        let mut map = serde_json::Map::new();
83        for (name, state) in states.iter() {
84            map.insert(name.clone(), state.clone());
85        }
86        Value::Object(map)
87    }
88
89    // ---- Events ----
90
91    /// Access the global event emitter.
92    pub fn events(&self) -> &EventEmitter {
93        &self.events
94    }
95
96    // ---- Router ----
97
98    /// Set the router instance.
99    pub fn set_router(&self, router: Arc<crate::router::HypenRouter>) {
100        *self.router.lock().unwrap() = Some(router);
101    }
102
103    /// Get the router, if one has been configured.
104    pub fn router(&self) -> Option<Arc<crate::router::HypenRouter>> {
105        self.router.lock().unwrap().clone()
106    }
107}
108
109impl Default for GlobalContext {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use serde_json::json;
119
120    #[test]
121    fn test_register_and_get_state() {
122        let ctx = GlobalContext::new();
123        ctx.register_module_state("counter", json!({"count": 5}));
124
125        let state = ctx.get_module_state("counter").unwrap();
126        assert_eq!(state["count"], 5);
127    }
128
129    #[test]
130    fn test_module_names() {
131        let ctx = GlobalContext::new();
132        ctx.register_module_state("a", json!({}));
133        ctx.register_module_state("b", json!({}));
134
135        let mut names = ctx.module_names();
136        names.sort();
137        assert_eq!(names, vec!["a", "b"]);
138    }
139
140    #[test]
141    fn test_unregister() {
142        let ctx = GlobalContext::new();
143        ctx.register_module_state("test", json!({}));
144        assert!(ctx.has_module("test"));
145
146        ctx.unregister_module("test");
147        assert!(!ctx.has_module("test"));
148    }
149
150    #[test]
151    fn test_global_state() {
152        let ctx = GlobalContext::new();
153        ctx.register_module_state("counter", json!({"count": 1}));
154        ctx.register_module_state("user", json!({"name": "Alice"}));
155
156        let global = ctx.global_state();
157        assert_eq!(global["counter"]["count"], 1);
158        assert_eq!(global["user"]["name"], "Alice");
159    }
160
161    #[test]
162    fn test_events_integration() {
163        use std::sync::atomic::{AtomicI32, Ordering};
164
165        let ctx = GlobalContext::new();
166        let count = Arc::new(AtomicI32::new(0));
167        let count_clone = count.clone();
168
169        ctx.events().on("test", move |_| {
170            count_clone.fetch_add(1, Ordering::SeqCst);
171        });
172
173        ctx.events().emit("test", &json!(null));
174        assert_eq!(count.load(Ordering::SeqCst), 1);
175    }
176}