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 ::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 { & ::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(clippy::fn_address_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 ::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 = &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                        ::classicube_sys::[<Event_Register $func_type>](
114                            handlers,
115                            ptr.cast(),
116                            Some(Self::callback),
117                        );
118
119                        self.registered = true;
120                    }
121                }
122
123                unsafe fn unregister_listener(&mut self) {
124                    if self.registered {
125                        let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
126                            self.callback_handler.as_mut();
127
128                        ::classicube_sys::[<Event_Unregister $func_type>](
129                            &mut ::classicube_sys::[<$event_type Events>].$event_name,
130                            ptr.cast(),
131                            Some(Self::callback),
132                        );
133
134                        self.registered = false;
135                    }
136                }
137
138                extern "C" fn callback(
139                    obj: *mut ::std::os::raw::c_void,
140                    $($name: $c_type, )*
141                ) {
142                    let event_handler = obj.cast::<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>();
143                    let event_handler = unsafe { &mut *event_handler };
144
145                    #[allow(clippy::redundant_closure_call)]
146                    let event = [<$event_name Event>] {
147                        $($name: ($to_rust)($name), )*
148                    };
149
150                    $crate::tracing::debug!("{} {:?}", stringify!([<$event_type>]), event);
151
152                    event_handler.handle_event(&event);
153                }
154            }
155
156            impl Drop for [<$event_name EventHandler>] {
157                fn drop(&mut self) {
158                    unsafe {
159                        self.unregister_listener();
160                    }
161                }
162            }
163
164            impl Default for [<$event_name EventHandler>] {
165                fn default() -> Self {
166                    Self::new()
167                }
168            }
169        }
170    };
171}
172
173pub fn reorder<T>(slice: &mut [T], old_index: usize, new_index: usize) {
174    match new_index.cmp(&old_index) {
175        Ordering::Less => {
176            slice[new_index..=old_index].rotate_right(1);
177        }
178        Ordering::Greater => {
179            slice[old_index..=new_index].rotate_left(1);
180        }
181        Ordering::Equal => {}
182    }
183}
184
185#[test]
186fn test_reorder() {
187    //           0  1  2  3  4  5  6  7  8  9
188    let mut a = [2, 3, 4, 5, 6, 7, 8, 0, 0, 0];
189    let count = 7;
190
191    // move 7 to where 4 is, shift 4,5,6 to the right
192    reorder(&mut a[..count], 5, 2);
193    //               0  1  2  3  4  5  6  7  8  9
194    assert_eq!(&a, &[2, 3, 7, 4, 5, 6, 8, 0, 0, 0]);
195
196    // move 7 to where 6 is, move 4,5,6 to the left
197    reorder(&mut a[..count], 2, 5);
198    //               0  1  2  3  4  5  6  7  8  9
199    assert_eq!(&a, &[2, 3, 4, 5, 6, 7, 8, 0, 0, 0]);
200
201    // move 2 to where 3 is, and 3 to where 2 is
202    reorder(&mut a[..count], 0, 1);
203    //               0  1  2  3  4  5  6  7  8  9
204    assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
205
206    // do nothing
207    reorder(&mut a[..count], 0, 0);
208    //               0  1  2  3  4  5  6  7  8  9
209    assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
210}
211
212#[macro_export]
213macro_rules! time {
214    ($title:tt, $block:block) => {{
215        let before = ::std::time::Instant::now();
216        let res = $block;
217        let after = ::std::time::Instant::now();
218        let diff = after - before;
219        debug!("{} ({:?})", $title, diff);
220        res
221    }};
222
223    ($title:expr, $high_millis:tt, $block:block) => {{
224        let before = ::std::time::Instant::now();
225        let res = $block;
226        let after = ::std::time::Instant::now();
227        let diff = after - before;
228        if diff > ::std::time::Duration::from_millis($high_millis) {
229            $crate::tracing::warn!("{} ({:?})", $title, diff);
230        } else {
231            $crate::tracing::debug!("{} ({:?})", $title, diff);
232        }
233        res
234    }};
235}
236
237#[macro_export]
238macro_rules! time_silent {
239    ($title:expr, $high_millis:tt, $block:block) => {{
240        let before = ::std::time::Instant::now();
241        let res = $block;
242        let after = ::std::time::Instant::now();
243        let diff = after - before;
244        if diff > ::std::time::Duration::from_millis($high_millis) {
245            $crate::tracing::warn!("{} ({:?})", $title, diff);
246        }
247        res
248    }};
249}
250
251#[macro_export]
252macro_rules! test_noop_fn {
253    ($name:tt) => {
254        #[cfg(test)]
255        #[no_mangle]
256        pub extern "C" fn $name() {}
257    };
258}
259
260#[macro_export]
261macro_rules! test_noop_static {
262    ($name:tt) => {
263        #[cfg(test)]
264        #[no_mangle]
265        pub static mut $name: () = ();
266    };
267}