1#![cfg_attr(docsrs, feature(doc_cfg))]
6
7#![allow(non_camel_case_types)]
15#![allow(non_upper_case_globals)]
16
17use std::os::raw::{c_char, c_int, c_void};
18
19pub const STDIO_BUS_EMBED_API_VERSION: c_int = 2;
21
22pub const STDIO_BUS_OK: c_int = 0;
24pub const STDIO_BUS_ERR: c_int = -1;
25pub const STDIO_BUS_EAGAIN: c_int = -2;
26pub const STDIO_BUS_EOF: c_int = -3;
27pub const STDIO_BUS_EFULL: c_int = -4;
28pub const STDIO_BUS_ENOTFOUND: c_int = -5;
29pub const STDIO_BUS_EINVAL: c_int = -6;
30
31pub const STDIO_BUS_ERR_CONFIG: c_int = -10;
33pub const STDIO_BUS_ERR_WORKER: c_int = -11;
34pub const STDIO_BUS_ERR_ROUTING: c_int = -12;
35pub const STDIO_BUS_ERR_BUFFER: c_int = -13;
36pub const STDIO_BUS_ERR_INVALID: c_int = -14;
37pub const STDIO_BUS_ERR_STATE: c_int = -15;
38
39#[repr(C)]
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum stdio_bus_state_t {
43 STDIO_BUS_STATE_CREATED = 0,
44 STDIO_BUS_STATE_STARTING = 1,
45 STDIO_BUS_STATE_RUNNING = 2,
46 STDIO_BUS_STATE_STOPPING = 3,
47 STDIO_BUS_STATE_STOPPED = 4,
48}
49
50#[repr(C)]
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum stdio_bus_listen_mode_t {
54 STDIO_BUS_LISTEN_NONE = 0,
55 STDIO_BUS_LISTEN_TCP = 1,
56 STDIO_BUS_LISTEN_UNIX = 2,
57}
58
59#[repr(C)]
61pub struct stdio_bus_t {
62 _private: [u8; 0],
63}
64
65pub type stdio_bus_message_cb = Option<
67 extern "C" fn(
68 bus: *mut stdio_bus_t,
69 msg: *const c_char,
70 len: usize,
71 user_data: *mut c_void,
72 ),
73>;
74
75pub type stdio_bus_error_cb = Option<
77 extern "C" fn(
78 bus: *mut stdio_bus_t,
79 code: c_int,
80 message: *const c_char,
81 user_data: *mut c_void,
82 ),
83>;
84
85pub type stdio_bus_log_cb = Option<
87 extern "C" fn(
88 bus: *mut stdio_bus_t,
89 level: c_int,
90 message: *const c_char,
91 user_data: *mut c_void,
92 ),
93>;
94
95pub type stdio_bus_worker_cb = Option<
97 extern "C" fn(
98 bus: *mut stdio_bus_t,
99 worker_id: c_int,
100 event: *const c_char,
101 user_data: *mut c_void,
102 ),
103>;
104
105pub type stdio_bus_client_connect_cb = Option<
107 extern "C" fn(
108 bus: *mut stdio_bus_t,
109 client_id: c_int,
110 peer_info: *const c_char,
111 user_data: *mut c_void,
112 ),
113>;
114
115pub type stdio_bus_client_disconnect_cb = Option<
117 extern "C" fn(
118 bus: *mut stdio_bus_t,
119 client_id: c_int,
120 reason: *const c_char,
121 user_data: *mut c_void,
122 ),
123>;
124
125#[repr(C)]
127pub struct stdio_bus_listener_config_t {
128 pub mode: stdio_bus_listen_mode_t,
129 pub tcp_host: *const c_char,
130 pub tcp_port: u16,
131 pub unix_path: *const c_char,
132}
133
134#[repr(C)]
136pub struct stdio_bus_options_t {
137 pub config_path: *const c_char,
138 pub config_json: *const c_char,
139 pub listener: stdio_bus_listener_config_t,
140 pub on_message: stdio_bus_message_cb,
141 pub on_error: stdio_bus_error_cb,
142 pub on_log: stdio_bus_log_cb,
143 pub on_worker: stdio_bus_worker_cb,
144 pub on_client_connect: stdio_bus_client_connect_cb,
145 pub on_client_disconnect: stdio_bus_client_disconnect_cb,
146 pub user_data: *mut c_void,
147 pub log_level: c_int,
148}
149
150#[repr(C)]
152#[derive(Debug, Clone, Default)]
153pub struct stdio_bus_stats_t {
154 pub messages_in: u64,
155 pub messages_out: u64,
156 pub bytes_in: u64,
157 pub bytes_out: u64,
158 pub worker_restarts: u64,
159 pub routing_errors: u64,
160 pub client_connects: u64,
161 pub client_disconnects: u64,
162}
163
164extern "C" {
165 pub fn stdio_bus_create(options: *const stdio_bus_options_t) -> *mut stdio_bus_t;
167
168 pub fn stdio_bus_start(bus: *mut stdio_bus_t) -> c_int;
170
171 pub fn stdio_bus_step(bus: *mut stdio_bus_t, timeout_ms: c_int) -> c_int;
173
174 pub fn stdio_bus_stop(bus: *mut stdio_bus_t, timeout_sec: c_int) -> c_int;
176
177 pub fn stdio_bus_destroy(bus: *mut stdio_bus_t);
179
180 pub fn stdio_bus_ingest(bus: *mut stdio_bus_t, msg: *const c_char, len: usize) -> c_int;
182
183 pub fn stdio_bus_get_state(bus: *const stdio_bus_t) -> stdio_bus_state_t;
185
186 pub fn stdio_bus_worker_count(bus: *const stdio_bus_t) -> c_int;
188
189 pub fn stdio_bus_session_count(bus: *const stdio_bus_t) -> c_int;
191
192 pub fn stdio_bus_pending_count(bus: *const stdio_bus_t) -> c_int;
194
195 pub fn stdio_bus_client_count(bus: *const stdio_bus_t) -> c_int;
197
198 pub fn stdio_bus_get_poll_fd(bus: *const stdio_bus_t) -> c_int;
200
201 pub fn stdio_bus_get_stats(bus: *const stdio_bus_t, stats: *mut stdio_bus_stats_t);
203}
204
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
215 fn test_api_version() {
216 assert_eq!(STDIO_BUS_EMBED_API_VERSION, 2);
217 }
218
219 #[test]
220 fn test_return_codes() {
221 assert_eq!(STDIO_BUS_OK, 0);
222 assert_eq!(STDIO_BUS_ERR, -1);
223 assert_eq!(STDIO_BUS_EAGAIN, -2);
224 assert_eq!(STDIO_BUS_EOF, -3);
225 assert_eq!(STDIO_BUS_EFULL, -4);
226 assert_eq!(STDIO_BUS_ENOTFOUND, -5);
227 assert_eq!(STDIO_BUS_EINVAL, -6);
228 }
229
230 #[test]
231 fn test_error_codes() {
232 assert_eq!(STDIO_BUS_ERR_CONFIG, -10);
233 assert_eq!(STDIO_BUS_ERR_WORKER, -11);
234 assert_eq!(STDIO_BUS_ERR_ROUTING, -12);
235 assert_eq!(STDIO_BUS_ERR_BUFFER, -13);
236 assert_eq!(STDIO_BUS_ERR_INVALID, -14);
237 assert_eq!(STDIO_BUS_ERR_STATE, -15);
238 }
239
240 #[test]
245 fn test_bus_state_values() {
246 assert_eq!(stdio_bus_state_t::STDIO_BUS_STATE_CREATED as i32, 0);
247 assert_eq!(stdio_bus_state_t::STDIO_BUS_STATE_STARTING as i32, 1);
248 assert_eq!(stdio_bus_state_t::STDIO_BUS_STATE_RUNNING as i32, 2);
249 assert_eq!(stdio_bus_state_t::STDIO_BUS_STATE_STOPPING as i32, 3);
250 assert_eq!(stdio_bus_state_t::STDIO_BUS_STATE_STOPPED as i32, 4);
251 }
252
253 #[test]
254 fn test_listen_mode_values() {
255 assert_eq!(stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_NONE as i32, 0);
256 assert_eq!(stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_TCP as i32, 1);
257 assert_eq!(stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_UNIX as i32, 2);
258 }
259
260 #[test]
261 fn test_bus_state_equality() {
262 let state1 = stdio_bus_state_t::STDIO_BUS_STATE_RUNNING;
263 let state2 = stdio_bus_state_t::STDIO_BUS_STATE_RUNNING;
264 let state3 = stdio_bus_state_t::STDIO_BUS_STATE_STOPPED;
265
266 assert_eq!(state1, state2);
267 assert_ne!(state1, state3);
268 }
269
270 #[test]
271 fn test_bus_state_copy() {
272 let state = stdio_bus_state_t::STDIO_BUS_STATE_RUNNING;
273 let copied = state;
274 assert_eq!(state, copied);
275 }
276
277 #[test]
278 fn test_bus_state_debug() {
279 let state = stdio_bus_state_t::STDIO_BUS_STATE_RUNNING;
280 let debug = format!("{:?}", state);
281 assert!(debug.contains("RUNNING"));
282 }
283
284 #[test]
289 fn test_stats_default() {
290 let stats = stdio_bus_stats_t::default();
291
292 assert_eq!(stats.messages_in, 0);
293 assert_eq!(stats.messages_out, 0);
294 assert_eq!(stats.bytes_in, 0);
295 assert_eq!(stats.bytes_out, 0);
296 assert_eq!(stats.worker_restarts, 0);
297 assert_eq!(stats.routing_errors, 0);
298 assert_eq!(stats.client_connects, 0);
299 assert_eq!(stats.client_disconnects, 0);
300 }
301
302 #[test]
303 fn test_stats_clone() {
304 let stats = stdio_bus_stats_t {
305 messages_in: 100,
306 messages_out: 50,
307 bytes_in: 1000,
308 bytes_out: 500,
309 worker_restarts: 2,
310 routing_errors: 1,
311 client_connects: 10,
312 client_disconnects: 5,
313 };
314
315 let cloned = stats.clone();
316 assert_eq!(cloned.messages_in, 100);
317 assert_eq!(cloned.messages_out, 50);
318 }
319
320 #[test]
321 fn test_stats_debug() {
322 let stats = stdio_bus_stats_t::default();
323 let debug = format!("{:?}", stats);
324 assert!(debug.contains("messages_in"));
325 }
326
327 #[test]
328 fn test_listener_config_size() {
329 let config = stdio_bus_listener_config_t {
331 mode: stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_NONE,
332 tcp_host: std::ptr::null(),
333 tcp_port: 0,
334 unix_path: std::ptr::null(),
335 };
336
337 assert_eq!(config.mode, stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_NONE);
338 assert_eq!(config.tcp_port, 0);
339 }
340
341 #[test]
342 fn test_options_struct() {
343 use std::ptr;
344
345 let listener = stdio_bus_listener_config_t {
346 mode: stdio_bus_listen_mode_t::STDIO_BUS_LISTEN_NONE,
347 tcp_host: ptr::null(),
348 tcp_port: 0,
349 unix_path: ptr::null(),
350 };
351
352 let options = stdio_bus_options_t {
353 config_path: ptr::null(),
354 config_json: ptr::null(),
355 listener,
356 on_message: None,
357 on_error: None,
358 on_log: None,
359 on_worker: None,
360 on_client_connect: None,
361 on_client_disconnect: None,
362 user_data: ptr::null_mut(),
363 log_level: 1,
364 };
365
366 assert_eq!(options.log_level, 1);
367 assert!(options.on_message.is_none());
368 }
369
370 #[test]
375 fn test_callback_types_are_option() {
376 let msg_cb: stdio_bus_message_cb = None;
378 let err_cb: stdio_bus_error_cb = None;
379 let log_cb: stdio_bus_log_cb = None;
380 let worker_cb: stdio_bus_worker_cb = None;
381 let connect_cb: stdio_bus_client_connect_cb = None;
382 let disconnect_cb: stdio_bus_client_disconnect_cb = None;
383
384 assert!(msg_cb.is_none());
385 assert!(err_cb.is_none());
386 assert!(log_cb.is_none());
387 assert!(worker_cb.is_none());
388 assert!(connect_cb.is_none());
389 assert!(disconnect_cb.is_none());
390 }
391
392 }