classicube_helpers/
macros.rs

1use std::cmp::Ordering;
2
3#[doc(hidden)]
4#[macro_export]
5macro_rules! make_event_handler {
6    (
7        $(#[$attr:meta])*
8        $event_type:ident,
9        $event_name:ident,
10        $func_type:ident,
11        (
12            $( {
13                name: $name:ident,
14                rust_type: $rust_type:ty,
15                c_type: $c_type:ty,
16                to_rust: $to_rust:expr,
17            }, )*
18        )
19    ) => {
20        paste::item! {
21            #[derive(Debug)]
22            pub struct [<$event_name Event>] {
23                $(pub $name: $rust_type, )*
24            }
25
26            $(#[$attr])*
27            pub struct [<$event_name EventHandler>] {
28                registered: bool,
29                callback_handler: Box<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>,
30            }
31
32            impl [<$event_name EventHandler>] {
33                #[must_use]
34                pub fn new() -> Self {
35                    Self {
36                        registered: false,
37                        callback_handler: Box::new($crate::callback_handler::CallbackHandler::new()),
38                    }
39                }
40
41                pub fn on<F>(&mut self, callback: F)
42                where
43                    F: FnMut(&[<$event_name Event>]),
44                    F: 'static,
45                {
46                    self.callback_handler.on(callback);
47
48                    unsafe {
49                        self.register_listener();
50                    }
51                }
52
53                // have to use c_type here because there's no way to use a converted OwnedString
54                // and keep it alive until after Raise() returns
55                pub fn raise(
56                    $($name: $c_type, )*
57                ) {
58                    unsafe {
59                        ::classicube_sys::[<Event_Raise $func_type>](
60                            &mut *&raw mut ::classicube_sys::[<$event_type Events>].$event_name,
61                            $($name, )*
62                        );
63                    }
64                }
65
66                #[must_use]
67                pub fn index(&self) -> Option<usize> {
68                    let event = unsafe { &*&raw const ::classicube_sys::[<$event_type Events>].$event_name };
69                    let ptr: *const $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
70                        self.callback_handler.as_ref();
71                    let ptr: *const ::std::os::raw::c_void = ptr.cast();
72
73                    #[allow(clippy::cast_sign_loss)]
74                    for i in 0..event.Count as usize {
75                        let handler = event.Handlers[i];
76                        let obj: *const ::std::os::raw::c_void = event.Objs[i].cast();
77                        #[allow(unpredictable_function_pointer_comparisons)]
78                        if handler
79                            .map(|handler| handler == Self::callback)
80                            .unwrap_or(false)
81                            && obj == ptr
82                        {
83                            return Some(i);
84                        }
85                    }
86
87                    None
88                }
89
90                /// Reorder by index
91                #[must_use]
92                pub fn reorder(&self, new_index: usize) -> Option<()> {
93                    let old_index = self.index()?;
94
95                    let event = unsafe { &mut *&raw mut ::classicube_sys::[<$event_type Events>].$event_name };
96                    #[allow(clippy::cast_sign_loss)]
97                    let count = event.Count as usize;
98
99                    let handlers = &mut event.Handlers[..count];
100                    let objs = &mut event.Objs[..count];
101                    $crate::macros::reorder(handlers, old_index, new_index);
102                    $crate::macros::reorder(objs, old_index, new_index);
103
104                    Some(())
105                }
106
107                unsafe fn register_listener(&mut self) {
108                    if !self.registered {
109                        let handlers = unsafe { &mut *&raw mut ::classicube_sys::[<$event_type Events>].$event_name };
110                        let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
111                            self.callback_handler.as_mut();
112
113                        unsafe {
114                            ::classicube_sys::[<Event_Register $func_type>](
115                                handlers,
116                                ptr.cast(),
117                                Some(Self::callback),
118                            );
119                        }
120
121                        self.registered = true;
122                    }
123                }
124
125                unsafe fn unregister_listener(&mut self) {
126                    if self.registered {
127                        let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
128                            self.callback_handler.as_mut();
129
130                        unsafe {
131                            ::classicube_sys::[<Event_Unregister $func_type>](
132                                &mut *&raw mut ::classicube_sys::[<$event_type Events>].$event_name,
133                                ptr.cast(),
134                                Some(Self::callback),
135                            );
136                        }
137
138                        self.registered = false;
139                    }
140                }
141
142                extern "C" fn callback(
143                    obj: *mut ::std::os::raw::c_void,
144                    $($name: $c_type, )*
145                ) {
146                    let event_handler = obj.cast::<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>();
147                    let event_handler = unsafe { &mut *event_handler };
148
149                    #[allow(clippy::redundant_closure_call)]
150                    let event = [<$event_name Event>] {
151                        $($name: ($to_rust)($name), )*
152                    };
153
154                    $crate::tracing::debug!("{} {:?}", stringify!([<$event_type>]), event);
155
156                    event_handler.handle_event(&event);
157                }
158            }
159
160            impl Drop for [<$event_name EventHandler>] {
161                fn drop(&mut self) {
162                    unsafe {
163                        self.unregister_listener();
164                    }
165                }
166            }
167
168            impl Default for [<$event_name EventHandler>] {
169                fn default() -> Self {
170                    Self::new()
171                }
172            }
173        }
174    };
175}
176
177pub fn reorder<T>(slice: &mut [T], old_index: usize, new_index: usize) {
178    match new_index.cmp(&old_index) {
179        Ordering::Less => {
180            slice[new_index..=old_index].rotate_right(1);
181        }
182        Ordering::Greater => {
183            slice[old_index..=new_index].rotate_left(1);
184        }
185        Ordering::Equal => {}
186    }
187}
188
189#[test]
190fn test_reorder() {
191    //           0  1  2  3  4  5  6  7  8  9
192    let mut a = [2, 3, 4, 5, 6, 7, 8, 0, 0, 0];
193    let count = 7;
194
195    // move 7 to where 4 is, shift 4,5,6 to the right
196    reorder(&mut a[..count], 5, 2);
197    //               0  1  2  3  4  5  6  7  8  9
198    assert_eq!(&a, &[2, 3, 7, 4, 5, 6, 8, 0, 0, 0]);
199
200    // move 7 to where 6 is, move 4,5,6 to the left
201    reorder(&mut a[..count], 2, 5);
202    //               0  1  2  3  4  5  6  7  8  9
203    assert_eq!(&a, &[2, 3, 4, 5, 6, 7, 8, 0, 0, 0]);
204
205    // move 2 to where 3 is, and 3 to where 2 is
206    reorder(&mut a[..count], 0, 1);
207    //               0  1  2  3  4  5  6  7  8  9
208    assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
209
210    // do nothing
211    reorder(&mut a[..count], 0, 0);
212    //               0  1  2  3  4  5  6  7  8  9
213    assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
214}
215
216#[macro_export]
217macro_rules! time {
218    ($title:tt, $block:block) => {{
219        let before = ::std::time::Instant::now();
220        let res = $block;
221        let after = ::std::time::Instant::now();
222        let diff = after - before;
223        debug!("{} ({:?})", $title, diff);
224        res
225    }};
226
227    ($title:expr, $high_millis:tt, $block:block) => {{
228        let before = ::std::time::Instant::now();
229        let res = $block;
230        let after = ::std::time::Instant::now();
231        let diff = after - before;
232        if diff > ::std::time::Duration::from_millis($high_millis) {
233            $crate::tracing::warn!("{} ({:?})", $title, diff);
234        } else {
235            $crate::tracing::debug!("{} ({:?})", $title, diff);
236        }
237        res
238    }};
239}
240
241#[macro_export]
242macro_rules! time_silent {
243    ($title:expr, $high_millis:tt, $block:block) => {{
244        let before = ::std::time::Instant::now();
245        let res = $block;
246        let after = ::std::time::Instant::now();
247        let diff = after - before;
248        if diff > ::std::time::Duration::from_millis($high_millis) {
249            $crate::tracing::warn!("{} ({:?})", $title, diff);
250        }
251        res
252    }};
253}
254
255#[macro_export]
256macro_rules! test_noop_fn {
257    ($name:tt) => {
258        #[cfg(test)]
259        #[allow(non_snake_case)]
260        #[unsafe(no_mangle)]
261        pub extern "C" fn $name() {}
262    };
263}
264
265#[macro_export]
266macro_rules! test_noop_static {
267    ($name:tt) => {
268        #[cfg(test)]
269        #[allow(non_upper_case_globals)]
270        #[unsafe(no_mangle)]
271        pub static mut $name: () = ();
272    };
273}