1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

//! The `CallbackData` object holds all the information about a callback
//! needed to manage the `user_data` and  invoke it safely and correctly.
//! The objects of this module are used internally. This file also contains
//! type declarations for the Rust-facing callback signatures.

use crate::hook::Hook;
use crate::hexchat::{ Hexchat, Eat, EventAttrs };
use crate::user_data::*;

use core::mem;

use UCallback::*;

/// Holds the Rust-implemented function, or closure, of a registered Hexchat
/// callback.
///
#[allow(dead_code)]
enum UCallback {
    Command     (Box< Callback >           ),
    Print       (Box< PrintCallback >      ),
    PrintAttrs  (Box< PrintAttrsCallback > ),
    Timer       (Box< TimerCallback >      ),
    TimerOnce   (Box< TimerCallbackOnce >  ),
    FD          (Box< FdCallback >         ),
    OnceDone,
}
impl Default for UCallback {
    /// Supports `mem::take()` for the `TimerOnce` callback invocation.
    /// The value of that callback is replaced with `OnceDone` when
    /// `mem::take()` is performed on it in `timer_once_cb()`.
    fn default() -> Self { OnceDone }
}


/// Pointers to instances of this struct are registered with the Hexchat
/// callbacks. On the C-facing side, this is the `user_data` passed to the
/// native callback wrappers (like `c_print_callback()`). When invoked by
/// Hexchat, the native callbacks receive a pointer to a `user_data`
/// (`CallbackData`) object, which the wrapper then uses to invoke the
/// Rust-implemented callback held in the `UCallback` field below. The `data`
/// field holds the user data registered for the Rust-facing callback, and is
/// passed to it when invoked.
pub (crate)
struct CallbackData {
    callback    : UCallback,
    data        : UserData,
    hook        : Hook,
}

impl CallbackData {
    /// Creates callback data for a regular command or server command.
    pub (crate)
    fn new_command_data(callback : Box<Callback>,
                        data     : UserData,
                        hook     : Hook) 
        -> Self
    {
        let callback = Command(callback);
        CallbackData { callback, data, hook  }
    }

    /// Creates callback data for a print callback.
    pub (crate)
    fn new_print_data(callback  : Box<PrintCallback>,
                      data      : UserData,
                      hook      : Hook) 
        -> Self
    {
        let callback = Print(callback);
        CallbackData { callback, data, hook }
    }

    /// Creates callback data for a print attrs callback.
    pub (crate)
    fn new_print_attrs_data(callback : Box<PrintAttrsCallback>,
                            data     : UserData,
                            hook     : Hook) 
        -> Self
    {
        let callback = PrintAttrs(callback);
        CallbackData { callback, data, hook }
    }

    /// Creates callback data for a timer callback.
    pub (crate)
    fn new_timer_data(callback : Box<TimerCallback>,
                      data     : UserData,
                      hook     : Hook) 
        -> Self
    {
        let callback = Timer(callback);
        CallbackData { callback, data, hook }
    }

    #[allow(dead_code)]
    pub (crate)
    fn new_timer_once_data(callback : Box<TimerCallbackOnce>,
                           data     : UserData,
                           hook     : Hook) 
        -> Self
    {
        let callback = TimerOnce(callback);
        CallbackData { callback, data, hook }
    }


    /// Creates callback data for a fd callback.
    pub (crate)
    fn new_fd_data(callback : Box<FdCallback>,
                   data     : UserData,
                   hook     : Hook) 
        -> Self
    {
        let callback = FD(callback);
        CallbackData { callback, data, hook }
    }

    /// Returns a reference to the Rust-facing `user_data` that was
    /// registered with the callback.
    #[inline]
    pub (crate)
    fn get_user_data(&self) -> &UserData {
        &self.data
    }

    /// Returns the `user_data` held by the `CallbackData` object, passing
    /// ownership to the caller. The data field in the `CallbackData` object is
    /// replaced with `NoData`.
    ///
    #[allow(dead_code)]
    pub (crate)
    fn take_data(&mut self) -> UserData {
        mem::take(&mut self.data)
    }

    /// Invokes the callback held in the `callback` field.
    #[inline]
    pub (crate)
    unsafe fn command_cb(&mut self,
                         hc       : &Hexchat,
                         word     : &[String],
                         word_eol : &[String],
                         ud       : &UserData) 
        -> Eat
    {
        if let Command(callback) = &mut self.callback {
            (*callback)(hc, word, word_eol, ud)
        } else {
            panic!("Invoked wrong type in CallbackData.");
        }
    }

    /// Invokes the callback held in the `callback` field. This is invoked by
    /// `c_print_callback()` which is the C-side registered callback for each
    /// print callback.
    #[inline]
    pub (crate)
    unsafe fn print_cb(&mut self,
                       hc       : &Hexchat,
                       word     : &[String],
                       ud       : &UserData) 
        -> Eat
    {
        if let Print(callback) = &mut self.callback {
            (*callback)(hc, word, ud)
        } else {
            panic!("Invoked wrong type in CallbackData.");
        }
    }

    /// Invokes the callback held in the `callback` field. This is invoked by
    /// `c_print_attrs_callback()`.
    #[inline]
    pub (crate)
    unsafe fn print_attrs_cb(&mut self,
                             hc    : &Hexchat,
                             word  : &[String],
                             attrs : &EventAttrs,
                             ud    : &UserData) 
        -> Eat
    {
        if let PrintAttrs(callback) = &mut self.callback {
            (*callback)(hc, word, attrs, ud)
        } else {
            panic!("Invoked wrong type in CallbackData.");
        }
    }

    /// Invokes the callback held in the `callback` field. This is invoked by
    /// c_timer_callback()`.
    #[inline]
    pub (crate)
    unsafe fn timer_cb(&mut self, hc: &Hexchat, ud: &UserData) -> i32
    {
        if let Timer(callback) = &mut self.callback {
            let keep_going = (*callback)(hc, ud);
            if keep_going == 0 {
                self.hook.unhook();
                0
            } else {
                1
            }
        } else {
            panic!("Invoked wrong type in CallbackData.");
        }
    }

    /// One time use timer callback. This is a special case; it's used for
    /// invoking callbacks on the main thread from other threads. It will
    /// unhook itself after one use.
    #[inline]
    pub (crate)
    unsafe fn timer_once_cb(&mut self, hc: &Hexchat, ud: &UserData) -> i32
    {
        let variant = mem::take(&mut self.callback);
        match variant {
            TimerOnce(callback) => {
                (callback)(hc, ud);
                self.hook.unhook();
                0
            },
            OnceDone => {
                panic!("Invoked a one-time callback more than once.");
            },
            _ => {
                panic!("Invoked wrong type in CallbackData.");
            },
        }
    }


    /// Invokes the callback held in the `callback` field. This is invoked by
    /// c_fd_callback()`.
    #[inline]
    pub (crate)
    unsafe fn fd_cb(&mut self,
                    hc    : &Hexchat,
                    fd    : i32,
                    flags : i32,
                    ud    : &UserData) 
        -> Eat
    {
        if let FD(callback) = &mut self.callback {
            (*callback)(hc, fd, flags, ud)
        } else {
            panic!("Invoked wrong type in CallbackData.");
        }
    }
}

/// The Rust-facing function signature corresponding to the C-facing
/// `C_Callback`. Note that, unlike the C API, the Rust-facing callback
/// signatures include a reference to the Hexchat pointer for
/// convenience.
pub (crate)
type Callback = dyn FnMut(&Hexchat,
                          &[String],
                          &[String],
                          &UserData
                         ) -> Eat;

/// The Rust-facing function signature corresponding to the C-facing
/// `C_PrintCallback`. Note that, unlike the C API, the Rust-facing callback
/// signatures include a reference to the Hexchat pointer for
/// convenience.
pub (crate)
type PrintCallback
              = dyn FnMut(&Hexchat,
                          &[String],
                          &UserData
                         ) -> Eat;

/// The Rust-facing function signature corresponding to the C-facing
/// `C_PrintAttrsCallback`. Note that, unlike the C API, the Rust-facing callback
/// signatures include a reference to the Hexchat pointer for
/// convenience.
pub (crate)
type PrintAttrsCallback
              = dyn FnMut(&Hexchat,
                          &[String],
                          &EventAttrs,
                          &UserData
                         ) -> Eat;

/// The Rust-facing function signature corresponding to the C-facing
/// `C_TimerCallback`. Note that, unlike the C API, the Rust-facing callback
/// signatures include a reference to the Hexchat pointer for
/// convenience.
pub (crate)
type TimerCallback
              = dyn FnMut(&Hexchat, &UserData) -> i32;

pub (crate)
type TimerCallbackOnce
              = dyn FnOnce(&Hexchat, &UserData) -> i32;


/// The Rust-facing function signature corresponding to the C-facing
/// `C_FdCallback`. Note that, unlike the C API, the Rust-facing callback
/// signatures include a reference to the Hexchat pointer for
/// convenience.
pub (crate)
type FdCallback
              = dyn FnMut(&Hexchat, i32, i32, &UserData) -> Eat;