subetha_cxc/
pass_registry.rs1use std::collections::HashMap;
16use std::sync::RwLock;
17
18use once_cell::sync::Lazy;
19
20#[derive(Debug, Clone)]
22pub struct Pass {
23 pub closure_id: u32,
24 pub args: Vec<u8>,
25}
26
27pub type PassResult = Result<Vec<u8>, PassError>;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum PassError {
33 UnknownClosureId(u32),
34 ExecutionError(String),
35}
36
37pub type PassHandler = Box<dyn Fn(&[u8]) -> PassResult + Send + Sync + 'static>;
40
41struct Registry {
42 handlers: HashMap<u32, PassHandler>,
43}
44
45static REGISTRY: Lazy<RwLock<Registry>> = Lazy::new(|| RwLock::new(Registry {
46 handlers: HashMap::new(),
47}));
48
49pub fn register<F>(id: u32, f: F) -> Option<PassHandler>
52where F: Fn(&[u8]) -> PassResult + Send + Sync + 'static,
53{
54 let mut g = REGISTRY.write().expect("registry write lock poisoned");
55 g.handlers.insert(id, Box::new(f))
56}
57
58pub fn unregister(id: u32) -> Option<PassHandler> {
60 let mut g = REGISTRY.write().expect("registry write lock poisoned");
61 g.handlers.remove(&id)
62}
63
64pub fn is_registered(id: u32) -> bool {
66 let g = REGISTRY.read().expect("registry read lock poisoned");
67 g.handlers.contains_key(&id)
68}
69
70pub fn registered_count() -> usize {
72 let g = REGISTRY.read().expect("registry read lock poisoned");
73 g.handlers.len()
74}
75
76pub fn execute(pass: &Pass) -> PassResult {
79 let g = REGISTRY.read().expect("registry read lock poisoned");
80 match g.handlers.get(&pass.closure_id) {
81 Some(handler) => handler(&pass.args),
82 None => Err(PassError::UnknownClosureId(pass.closure_id)),
83 }
84}
85
86#[macro_export]
90macro_rules! register_pass {
91 ($id:expr, $name:expr, $handler:expr) => {{
92 drop($crate::pass_registry::register($id, $handler));
97 let _name = $name; }};
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn register_then_execute_round_trips_bytes() {
107 let id = 0x1000_0001;
110 register(id, |args| {
111 let mut out = args.to_vec();
112 out.reverse();
113 Ok(out)
114 });
115 let pass = Pass { closure_id: id, args: b"hello".to_vec() };
116 let r = execute(&pass).unwrap();
117 assert_eq!(r, b"olleh");
118 unregister(id);
119 }
120
121 #[test]
122 fn unknown_closure_id_returns_error() {
123 let pass = Pass { closure_id: 0xDEAD_BEEF, args: vec![] };
124 assert_eq!(execute(&pass), Err(PassError::UnknownClosureId(0xDEAD_BEEF)));
125 }
126
127 #[test]
128 fn re_register_overwrites_previous() {
129 let id = 0x1000_0002;
130 register(id, |_| Ok(b"v1".to_vec()));
131 register(id, |_| Ok(b"v2".to_vec()));
132 let pass = Pass { closure_id: id, args: vec![] };
133 assert_eq!(execute(&pass).unwrap(), b"v2");
134 unregister(id);
135 }
136
137 #[test]
138 fn handler_can_return_execution_error() {
139 let id = 0x1000_0003;
140 register(id, |_| Err(PassError::ExecutionError("nope".to_string())));
141 let pass = Pass { closure_id: id, args: vec![] };
142 match execute(&pass) {
143 Err(PassError::ExecutionError(msg)) => assert_eq!(msg, "nope"),
144 other => panic!("expected ExecutionError, got {other:?}"),
145 }
146 unregister(id);
147 }
148
149 #[test]
150 fn is_registered_and_count_accurate() {
151 let id_a = 0x1000_0004;
152 let id_b = 0x1000_0005;
153 register(id_a, |_| Ok(vec![]));
154 register(id_b, |_| Ok(vec![]));
155 assert!(is_registered(id_a));
156 assert!(is_registered(id_b));
157 assert!(registered_count() >= 2);
162 unregister(id_a);
163 unregister(id_b);
164 assert!(!is_registered(id_a));
165 }
166
167 #[test]
168 fn macro_registration_works() {
169 let id = 0x1000_0006;
170 register_pass!(id, "test_pass", |args: &[u8]| {
171 Ok(args.iter().map(|b| b.wrapping_add(1)).collect())
172 });
173 assert!(is_registered(id));
174 let pass = Pass { closure_id: id, args: vec![1, 2, 3] };
175 assert_eq!(execute(&pass).unwrap(), vec![2, 3, 4]);
176 unregister(id);
177 }
178}