Skip to main content

ironsbe_server/
handler.rs

1//! Message handler traits.
2
3use ironsbe_core::header::MessageHeader;
4
5/// Trait for handling incoming SBE messages.
6pub trait MessageHandler: Send + Sync {
7    /// Called when a complete SBE message is received.
8    ///
9    /// # Arguments
10    /// * `session_id` - ID of the session that sent the message
11    /// * `header` - Decoded message header
12    /// * `buffer` - Full message buffer (including header)
13    /// * `responder` - Interface for sending responses
14    fn on_message(
15        &self,
16        session_id: u64,
17        header: &MessageHeader,
18        buffer: &[u8],
19        responder: &dyn Responder,
20    );
21
22    /// Called when a new session is established.
23    ///
24    /// # Arguments
25    /// * `session_id` - ID of the new session
26    fn on_session_start(&self, _session_id: u64) {}
27
28    /// Called when a session ends.
29    ///
30    /// # Arguments
31    /// * `session_id` - ID of the ended session
32    fn on_session_end(&self, _session_id: u64) {}
33
34    /// Called on decode error.
35    ///
36    /// # Arguments
37    /// * `session_id` - ID of the session
38    /// * `error` - Description of the error
39    fn on_error(&self, _session_id: u64, _error: &str) {}
40}
41
42/// Responder for sending messages back to clients.
43pub trait Responder: Send + Sync {
44    /// Sends a message to the current session.
45    ///
46    /// # Arguments
47    /// * `message` - Message bytes to send
48    ///
49    /// # Errors
50    /// Returns error if send fails.
51    fn send(&self, message: &[u8]) -> Result<(), SendError>;
52
53    /// Sends a message to a specific session.
54    ///
55    /// # Arguments
56    /// * `session_id` - Target session ID
57    /// * `message` - Message bytes to send
58    ///
59    /// # Errors
60    /// Returns error if send fails.
61    fn send_to(&self, session_id: u64, message: &[u8]) -> Result<(), SendError>;
62}
63
64/// Error type for send operations.
65#[derive(Debug, Clone)]
66pub struct SendError {
67    /// Error message.
68    pub message: String,
69}
70
71impl std::fmt::Display for SendError {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(f, "send error: {}", self.message)
74    }
75}
76
77impl std::error::Error for SendError {}
78
79/// Handler for specific message type.
80pub trait TypedHandler: Send + Sync {
81    /// Handles a message of the specific type.
82    ///
83    /// # Arguments
84    /// * `session_id` - ID of the session
85    /// * `buffer` - Message buffer (after header)
86    /// * `responder` - Interface for sending responses
87    fn handle(&self, session_id: u64, buffer: &[u8], responder: &dyn Responder);
88}
89
90/// Wrapper to convert a closure into a TypedHandler.
91pub struct FnHandler<F> {
92    handler: F,
93}
94
95impl<F> FnHandler<F>
96where
97    F: Fn(u64, &[u8], &dyn Responder) + Send + Sync,
98{
99    /// Creates a new function handler.
100    pub fn new(handler: F) -> Self {
101        Self { handler }
102    }
103}
104
105impl<F> TypedHandler for FnHandler<F>
106where
107    F: Fn(u64, &[u8], &dyn Responder) + Send + Sync,
108{
109    fn handle(&self, session_id: u64, buffer: &[u8], responder: &dyn Responder) {
110        (self.handler)(session_id, buffer, responder);
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use std::sync::Arc;
118    use std::sync::atomic::{AtomicBool, Ordering};
119
120    #[test]
121    fn test_send_error_display() {
122        let err = SendError {
123            message: "connection lost".to_string(),
124        };
125        let msg = err.to_string();
126        assert!(msg.contains("connection lost"));
127        assert!(msg.contains("send error"));
128    }
129
130    #[test]
131    fn test_send_error_clone() {
132        let err = SendError {
133            message: "test".to_string(),
134        };
135        let cloned = err.clone();
136        assert_eq!(err.message, cloned.message);
137    }
138
139    #[test]
140    fn test_send_error_debug() {
141        let err = SendError {
142            message: "debug test".to_string(),
143        };
144        let debug_str = format!("{:?}", err);
145        assert!(debug_str.contains("SendError"));
146        assert!(debug_str.contains("debug test"));
147    }
148
149    struct MockResponder;
150
151    impl Responder for MockResponder {
152        fn send(&self, _message: &[u8]) -> Result<(), SendError> {
153            Ok(())
154        }
155
156        fn send_to(&self, _session_id: u64, _message: &[u8]) -> Result<(), SendError> {
157            Ok(())
158        }
159    }
160
161    #[test]
162    fn test_fn_handler() {
163        let called = Arc::new(AtomicBool::new(false));
164        let called_clone = called.clone();
165
166        let handler = FnHandler::new(move |_session_id, _buffer, _responder| {
167            called_clone.store(true, Ordering::SeqCst);
168        });
169
170        let responder = MockResponder;
171        handler.handle(1, &[1, 2, 3], &responder);
172
173        assert!(called.load(Ordering::SeqCst));
174    }
175
176    struct TestMessageHandler {
177        session_started: Arc<AtomicBool>,
178        session_ended: Arc<AtomicBool>,
179        error_received: Arc<AtomicBool>,
180    }
181
182    impl MessageHandler for TestMessageHandler {
183        fn on_message(
184            &self,
185            _session_id: u64,
186            _header: &MessageHeader,
187            _buffer: &[u8],
188            _responder: &dyn Responder,
189        ) {
190        }
191
192        fn on_session_start(&self, _session_id: u64) {
193            self.session_started.store(true, Ordering::SeqCst);
194        }
195
196        fn on_session_end(&self, _session_id: u64) {
197            self.session_ended.store(true, Ordering::SeqCst);
198        }
199
200        fn on_error(&self, _session_id: u64, _error: &str) {
201            self.error_received.store(true, Ordering::SeqCst);
202        }
203    }
204
205    #[test]
206    fn test_message_handler_callbacks() {
207        let handler = TestMessageHandler {
208            session_started: Arc::new(AtomicBool::new(false)),
209            session_ended: Arc::new(AtomicBool::new(false)),
210            error_received: Arc::new(AtomicBool::new(false)),
211        };
212
213        handler.on_session_start(1);
214        assert!(handler.session_started.load(Ordering::SeqCst));
215
216        handler.on_session_end(1);
217        assert!(handler.session_ended.load(Ordering::SeqCst));
218
219        handler.on_error(1, "test error");
220        assert!(handler.error_received.load(Ordering::SeqCst));
221    }
222}