hexchat_api/
hexchat.rs

1//! This file holds the Hexchat struct used to interface with Hexchat. Its
2//! fields are the actual callbacks provided by Hexchat. When Hexchat
3//! loads this library, the Hexchat pointer is stored and used by casting it
4//! to the struct contained in this file. These native function pointers are
5//! private to this crate, and a more Rust-friendly API is provided through
6//! this Hexchat interface.
7
8use libc::{c_int, c_char, c_void, time_t};
9use std::ptr;
10use std::ffi::CString;
11use std::fmt;
12use std::fmt::Debug;
13use std::ops::FnMut;
14use std::ptr::null;
15use std::str;
16
17use crate::callback_data::{CallbackData, TimerCallbackOnce};
18use crate::context::Context;
19use crate::{hexchat_callbacks::*, HexchatError};
20#[cfg(feature = "threadsafe")]
21use crate::hexchat_entry_points::PHEXCHAT;
22use crate::hook::Hook;
23use crate::list_iterator::ListIterator;
24use crate::plugin::Plugin;
25use crate::user_data::*;
26use crate::utils::*;
27#[cfg(feature = "threadsafe")]
28use crate::threadsafe_hexchat::*;
29
30/// Value used in example from the Hexchat Plugin Interface doc web page.
31const MAX_PREF_VALUE_SIZE: usize =  512;
32
33/// Value specified on the [Hexchat Plugin Interface web page]
34/// (https://hexchat.readthedocs.io/en/latest/plugins.html).
35const MAX_PREF_LIST_SIZE : usize = 4096;
36
37// hexchat_send_modes, hexchat_event_attrs_free, pluginpref_delete,
38
39/// The priorty for a given callback invoked by Hexchat.
40pub enum Priority {
41    Highest     =  127,
42    High        =   64,
43    Norm        =    0,
44    Low         =  -64,
45    Lowest      = -128,
46}
47
48/// The return value for client plugin callbacks.
49pub enum Eat {
50    None        =    0,
51    Hexchat     =    1,
52    Plugin      =    2,
53    All         =    3,
54}
55
56/// File descriptor types.
57pub enum FD {
58    Read        =    1,
59    Write       =    2,
60    Exception   =    4,
61    NotSocket   =    8,
62}
63
64/// Used by the `hexthat.strip()` function to determine what to strip from the
65/// target string.
66pub enum StripFlags {
67    StripMIrcColors     = 1,
68    StripTextAttributes = 2,
69    StripBoth           = 3,
70}
71
72/// This is the rust-facing Hexchat API. Each method has a corresponding
73/// C function pointer which they wrap and marshal data/from.
74///
75impl Hexchat {
76
77    /// Returns a thread-safe wrapper for `Hexchat` that exposes thread-safe
78    /// methods wrapping several of `Hexchat`s methods.
79    ///
80    #[cfg(feature = "threadsafe")]
81    pub fn threadsafe(&self) -> ThreadSafeHexchat {
82        ThreadSafeHexchat::new(unsafe { &*PHEXCHAT })
83    }
84
85    /// Prints the string passed to it to the active Hexchat window.
86    /// # Arguments
87    /// * `text` - The text to print.
88    ///
89    pub fn print(&self, text: &str) {
90        let text = str2cstring(text);
91        unsafe { (self.c_print)(self, text.as_ptr()); }
92    }
93
94    /// Invokes the Hexchat command specified by `command`.
95    /// # Arguments
96    /// * `command` - The Hexchat command to invoke.
97    ///
98    pub fn command(&self, command: &str) {
99        let command = str2cstring(command);
100        unsafe { (self.c_command)(self, command.as_ptr()); }
101    }
102
103    /// Registeres a command callback with Hexchat. This will add a user
104    /// invocable slash "/" command that can be seen when listing `/help`.
105    /// The callback can be a static function, or a closure, that has the form:
106    /// ``` no_test
107    ///     FnMut(&Hexchat, &[String], &[String], &UserData) -> Eat
108    /// ```
109    /// Note that the callback parameters include a reference to the `Hexchat`
110    /// object as a convenience. This differs from the C interface which doesn't
111    /// include it.
112    ///
113    /// # Arguments
114    /// * `name`        - The name of the event that invokes the callback.
115    /// * `pri`         - The priority of the callback.
116    /// * `callback`    - The static function or closure to register.
117    /// * `help`        - Help text displayed by Hexchat for the command.
118    /// * `user_data`   - Data passed back to the callback when invoked.
119    ///
120    /// # Returns
121    /// A `Hook` object associated with the callback.
122    ///
123    pub fn hook_command<F>(&self,
124                           name        : &str,
125                           pri         : Priority,
126                           callback    : F,
127                           help        : &str,
128                           user_data   : UserData)
129        -> Hook
130    where
131        F: FnMut(&Hexchat, &[String], &[String], &UserData)
132           -> Eat + 'static
133    {
134        let hook = Hook::new();
135        let ud   = Box::new(
136                    CallbackData::new_command_data(
137                                      Box::new(callback),
138                                      user_data,
139                                      hook.clone()));
140
141        let ud   = Box::into_raw(ud) as *mut c_void;
142
143        hook.set_cbd(ud);
144
145        let help = if !help.is_empty() {
146            help
147        } else {
148            "No help available for this command."
149        };
150        let name = str2cstring(name);
151        let help = str2cstring(help);
152        unsafe {
153            // TODO - Consider making an empty help string cause a NULL to be
154            //        used as hook_command()'s 5th argument.
155            hook.set((self.c_hook_command)(self,
156                                           name.as_ptr(),
157                                           pri as i32,
158                                           c_callback,
159                                           help.as_ptr(),
160                                           ud));
161        }
162        hook
163    }
164
165    /// Registers a callback to be called when a certain server event occurs.
166    /// For any of these functions, more information can be found at
167    /// [Hexchat Plugin Interface](https://hexchat.readthedocs.io/en/latest/plugins.html)
168    /// The callback needs to be compatible with this signature:
169    ///  ``` no_test
170    ///  FnMut(&Hexchat, &[String], &[String], &UserData) -> Eat
171    ///  ```
172    /// # Arguments
173    /// * `name`        - The name of the event to listen for.
174    /// * `pri`         - The priority of the callback.
175    /// * `callback`    - The callback to invoke when the event occurs.
176    /// * `user_data`   - The user data that gets passed back to the callback
177    ///                   when it's invoked.
178    /// # Returns
179    /// * A `Hook` object that can be used to deregister the callback. It
180    ///   doesn't need to be retained if not needed.
181    ///
182    pub fn hook_server<F>(&self,
183                          name        : &str,
184                          pri         : Priority,
185                          callback    : F,
186                          user_data   : UserData)
187        -> Hook
188    where
189        F: FnMut(&Hexchat, &[String], &[String], &UserData)
190           -> Eat + 'static
191    {
192        let hook = Hook::new();
193        let ud   = Box::new(
194                    CallbackData::new_command_data(
195                                      Box::new(callback),
196                                      user_data,
197                                      hook.clone()
198                                  ));
199        let ud = Box::into_raw(ud) as *mut c_void;
200
201        hook.set_cbd(ud);
202        let name = str2cstring(name);
203        unsafe {
204            hook.set((self.c_hook_server)(self,
205                                          name.as_ptr(),
206                                          pri as i32,
207                                          c_callback,
208                                          ud));
209        }
210        hook
211    }
212
213    /// Registers a callback to be called when a given print event occurs. This
214    /// can be any of the text events listed under Settings > Text Events.
215    /// Callback needs to be compatible with this signature:
216    /// ``` no_test
217    /// FnMut(&Hexchat, &[String], &UserData) -> Eat
218    /// ```
219    /// # Arguments
220    /// * `name`        - The name of the event to listen for.
221    /// * `pri`         - The priority of the callback.
222    /// * `callback`    - The callback to invoke when the event occurs.
223    /// * `user_data`   - The user data that gets passed back to the callback
224    ///                   when it's invoked.
225    /// # Returns
226    /// * A `Hook` object that can be used to deregister the callback. It
227    ///   doesn't need to be retained if not needed.
228    ///
229    pub fn hook_print<F>(&self,
230                         event_name  : &str,
231                         pri         : Priority,
232                         callback    : F,
233                         user_data   : UserData)
234        -> Hook
235    where
236        F: FnMut(&Hexchat, &[String], &UserData) -> Eat + 'static
237    {
238        let hook = Hook::new();
239        let ud   = Box::new(
240                    CallbackData::new_print_data(
241                                      Box::new(callback),
242                                      user_data,
243                                      hook.clone()
244                                  ));
245        let ud = Box::into_raw(ud) as *mut c_void;
246
247        hook.set_cbd(ud);
248        let event_name = str2cstring(event_name);
249        unsafe {
250            hook.set((self.c_hook_print)(self,
251                                         event_name.as_ptr(),
252                                         pri as i32,
253                                         c_print_callback,
254                                         ud));
255        }
256        hook
257    }
258
259    /// Registers a callback to be called when a given print event occurs.
260    /// The callback will be invoked with an `EventAttrs` object containing
261    /// a `time_t` value for the event. The callback needs to be compatible
262    /// with this signature:
263    /// ``` no_test
264    /// FnMut(&Hexchat, &[String], &EventAttrs, &UserData) -> Eat
265    /// ```
266    /// # Arguments
267    /// * `name`        - The name of the event to listen for.
268    /// * `pri`         - The priority of the callback.
269    /// * `callback`    - The callback to invoke when the event occurs.
270    /// * `user_data`   - The user data that gets passed back to the callback
271    ///                   when it's invoked.
272    /// # Returns
273    /// * A `Hook` object that can be used to deregister the callback. It
274    ///   doesn't need to be retained if not needed.
275    ///
276    pub fn hook_print_attrs<F>(&self,
277                               name        : &str,
278                               pri         : Priority,
279                               callback    : F,
280                               user_data   : UserData)
281        -> Hook
282    where
283        F: FnMut(&Hexchat, &[String], &EventAttrs, &UserData)
284           -> Eat + 'static
285    {
286        let hook = Hook::new();
287        let ud   = Box::new(
288                    CallbackData::new_print_attrs_data(
289                                      Box::new(callback),
290                                      user_data,
291                                      hook.clone()
292                                  ));
293        let ud = Box::into_raw(ud) as *mut c_void;
294
295        hook.set_cbd(ud);
296        let name = str2cstring(name);
297        unsafe {
298            hook.set((self.c_hook_print_attrs)(self,
299                                               name.as_ptr(),
300                                               pri as i32,
301                                               c_print_attrs_callback,
302                                               ud));
303        }
304        hook
305    }
306
307
308    /// Sets up a callback to be invoked every `timeout` milliseconds. The
309    /// callback needs to be compatible with:
310    /// ``` no_test
311    /// FnMut(&Hexchat, &UserData) -> i32
312    /// ```
313    /// # Arguments
314    /// * `timeout`     - The timeout in milliseconds.
315    /// * `callback`    - The `FnOnce()` callback.
316    /// * `user_data`   - User data included with the callback and passed back
317    ///                   to the callback during invocation.
318    /// # Returns
319    /// * A `Hook` object that is can be used to deregister the callback.
320    ///
321    pub fn hook_timer<F>(&self,
322                         timeout   : i64,
323                         callback  : F,
324                         user_data : UserData)
325        -> Hook
326    where
327        F: FnMut(&Hexchat, &UserData) -> i32 + 'static
328    {
329        let hook = Hook::new();
330        let ud   = Box::new(CallbackData::new_timer_data(
331                                            Box::new(callback),
332                                            user_data,
333                                            hook.clone()
334                                        ));
335        let ud = Box::into_raw(ud) as *mut c_void;
336
337        hook.set_cbd(ud);
338
339        unsafe {
340            hook.set((self.c_hook_timer)(self,
341                                         timeout as c_int,
342                                         c_timer_callback,
343                                         ud));
344        }
345        hook
346    }
347
348    /// This is a special case feature, used internally to enable other threads
349    /// to invoke callbacks on the main thread. This function isn't exported
350    /// with the rest of the functions of this class.
351    /// # Arguments
352    /// * `timeout`     - The timeout in milliseconds.
353    /// * `callback`    - The `FnOnce()` callback.
354    /// * `user_data`   - User data included with the callback and passed back
355    ///                   to the callback during invocation.
356    /// # Returns
357    /// * A `Hook` object that is used to deregister the callback after it's
358    ///   invoked.
359    ///
360    #[allow(dead_code)]
361    pub (crate)
362    fn hook_timer_once(&self,
363                       timeout   : i64,
364                       callback  : Box<TimerCallbackOnce>,
365                       user_data : UserData)
366        -> Hook
367    {
368        // TODO - Put the function signatures somewhere logical (?)
369
370        let hook = Hook::new();
371        let ud   = Box::new(CallbackData::new_timer_once_data(
372                                            callback,
373                                            user_data,
374                                            hook.clone()
375                                        ));
376        let ud = Box::into_raw(ud) as *mut c_void;
377
378        hook.set_cbd(ud);
379
380        unsafe {
381            hook.set((self.c_hook_timer)(self,
382                                         timeout as c_int,
383                                         c_timer_callback_once,
384                                         ud));
385        }
386        hook
387    }
388
389    /// Registers a callback to be called after the given timeout.
390    pub fn hook_fd<F>(&self,
391                      fd        : i32,
392                      flags     : i32,
393                      callback  : F,
394                      user_data : UserData)
395        -> Hook
396    where
397        F: FnMut(&Hexchat, i32, i32, &UserData) -> Eat + 'static
398    {
399        let hook = Hook::new();
400        let ud   = Box::new(CallbackData::new_fd_data(
401                                            Box::new(callback),
402                                            user_data,
403                                            hook.clone()
404                                        ));
405        let ud = Box::into_raw(ud) as *mut c_void;
406
407        hook.set_cbd(ud);
408
409        unsafe {
410            hook.set((self.c_hook_fd)(self,
411                                      fd as c_int,
412                                      flags as c_int,
413                                      c_fd_callback,
414                                      ud));
415        }
416        hook
417    }
418
419    /// Unhooks any Hook that was returned from a callback registration.
420    /// Ownership of the user_data is transferred to the caller.
421    /// Note: Hexchat unhooks all hooks automatically when a plugin is unloaded,
422    /// so the client plugin doesn't have to in that case.
423    /// # Arguments
424    /// * `hook` - The callback hook to deregister with Hexchat.
425    /// # Returns
426    /// * The user data that was registered with the callback using one of the
427    ///   hook commands. Ownership of this object is transferred to the caller.
428    ///
429    pub fn unhook(&self, hook: &mut Hook) -> UserData {
430        hook.unhook()
431    }
432
433
434    /// Issues one of the Hexchat IRC events. The command works for any of the
435    /// events listed in Settings > Text Events dialog.
436    /// # Arguments
437    /// * `event_name`  - The name of the Hexchat text event to send.
438    /// * `var_args`    - A slice of `&str`'s containing the event's arguments.
439    /// # Returns
440    /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
441    ///
442    pub fn emit_print(&self, event_name: &str, var_args: &[&str])
443        -> Result<(), HexchatError>
444    {
445        let event_attrs = EventAttrs { server_time_utc: 0 };
446        self.emit_print_impl(0, &event_attrs, event_name, var_args)
447    }
448
449
450    /// Issues one of the Hexchat IRC events. The command works for any of the
451    /// events listed in Settings > Text Events dialog.
452    /// # Arguments
453    /// * `event_attrs` - A reference to an `EventAttrs` struct.
454    /// * `event_name`  - The name of the Hexchat text event to send.
455    /// * `var_args`    - A slice of `&str`'s containing the event's arguments.
456    /// # Returns
457    /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
458    ///
459    pub fn emit_print_attrs(&self,
460                            event_attrs : &EventAttrs,
461                            event_name  : &str,
462                            var_args    : &[&str])
463        -> Result<(), HexchatError>
464    {
465        self.emit_print_impl(1, event_attrs, event_name, var_args)
466    }
467
468    /// Issues one of the Hexchat IRC events. Called internally by the public
469    /// commands, `emit_print()` and `emit_print_attrs()`. The command works
470    /// for any of the events listed in Settings > Text Events dialog.
471    /// # Arguments
472    /// * `ver`         - 0 to invoke `hc.c_emit_print()`, 1 to invoke
473    ///                   `hc.c_emit_print_attrs()`.
474    /// * `event_attrs` - A reference to an `EventAttrs` struct.
475    /// * `event_name`  - The name of the Hexchat text event to send.
476    /// * `var_args`    - A slice of `&str`'s containing the event's arguments.
477    /// # Returns
478    /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
479    ///
480    fn emit_print_impl(&self,
481                       ver          : i32,
482                       event_attrs  : &EventAttrs,
483                       event_name   : &str,
484                       var_args     : &[&str])
485        -> Result<(), HexchatError>
486    {
487        let mut args   = vec![];
488        let     name   = str2cstring(event_name);
489        let     va_len = var_args.len();
490
491        // We don't know if there are 6 items in var_args - so Clippy's
492        // suggestion would fail. This range loop is fine.
493        #[allow(clippy::needless_range_loop)]
494        for i in 0..6 {
495            args.push(str2cstring(if i < va_len { var_args[i] } else { "" }));
496        }
497
498        // TODO - If empty strings don't suffice as a nop param, then construct
499        //        another vector containing pointers and pad with nulls.
500        unsafe {
501            use HexchatError::*;
502
503            if ver == 0 {
504                let result = (self.c_emit_print)(
505                                    self,
506                                    name.as_ptr(), args[0].as_ptr(),
507                                    args[1].as_ptr(), args[2].as_ptr(),
508                                    args[3].as_ptr(), args[4].as_ptr(),
509                                    args[5].as_ptr(), null::<c_char>());
510                if result > 0 {
511                    Ok(())
512                } else {
513                    Err(CommandFailed(format!("`.emit_print(\"{}\", {:?})` \
514                                              failed. Check the event name \
515                                              and data for errors.",
516                                              event_name, var_args)))
517                }
518            } else {
519                let result = (self.c_emit_print_attrs)(
520                                    self, event_attrs,
521                                    name.as_ptr(), args[0].as_ptr(),
522                                    args[1].as_ptr(), args[2].as_ptr(),
523                                    args[3].as_ptr(), args[4].as_ptr(),
524                                    args[5].as_ptr(), null::<c_char>());
525                if result > 0 {
526                    Ok(())
527                } else {
528                    Err(CommandFailed(format!(
529                                      "`.emit_print_attrs(\"{}\", {:?})` \
530                                      failed. Check the event name \
531                                      and data for errors.",
532                                      event_name, var_args)))
533                }
534            }
535        }
536    }
537
538    /// Compares two nicknames, returning a similar value to `strcmp()`.
539    /// If they're equal (0), s1 < s2 (<0 - negative), or s1 > s2 (>0 positive).
540    /// # Arguments
541    /// * `s1` - The first nickname to compare.
542    /// * `s2` - The second.
543    /// # Returns
544    /// * If the first non-matching character is of lesser value for `s1`, a
545    ///   negative value is returned; if `s1`'s char is greater, then a non-0
546    ///   postive value is returned. 0 is returned if they match.
547    ///
548    pub fn nickcmp(&self, s1: &str, s2: &str) -> i32 {
549        let s1 = str2cstring(s1);
550        let s2 = str2cstring(s2);
551        unsafe {
552            (self.c_nickcmp)(self, s1.as_ptr(), s2.as_ptr())
553        }
554    }
555
556    /// Converts a string with text attributes and IRC colors embedded into
557    /// a plain text string. Either IRC colors, or text attributes (or both)
558    /// can be stripped out of the string.
559    /// # Arguments
560    /// * `text`    - The string to strip.
561    /// * `flags`   - One of the `StripFlags` cases (`StripMIrcColors`,
562    ///               `StripTextAttributes`, `StripBoth`).
563    /// # Returns
564    /// * `Some(<stripped-string>)` or `None` if the operation failed.
565    ///
566    pub fn strip(&self, text: &str, flags: StripFlags) -> Option<String> {
567        let length = text.len() as i32;
568        let text  = str2cstring(text);
569        let result = unsafe {
570            (self.c_strip)(self, text.as_ptr(), length, flags as i32)
571        };
572        if !result.is_null() {
573            let stripped = pchar2string(result);
574            unsafe { (self.c_free)(self, result as *const c_void); }
575            Some(stripped)
576        } else { None }
577    }
578
579    /// Sets the currently active context to that bound to the  `Context`
580    /// object. The contexts are essentially the channels the user is in
581    /// and has open tabs/windows to them. The `Context` object itself has
582    /// a `.set()` method that can be invoked directly, which this command
583    /// invokes.
584    /// # Arguments
585    /// * `context` - The `Context` to make the currently active context.
586    /// # Returns
587    /// * A result (`Result<(), HexchatError>) where `Ok(())` indicates
588    ///   the context has been switched, and a `HexchatError` if it didn't.
589    ///
590    pub fn set_context(&self, context: &Context) -> Result<(), HexchatError> {
591        context.set()
592    }
593
594    /// Returns a `Context` object bound to the requested server/channel.
595    /// The object provides methods like `print()` that will execute the
596    /// Hexchat print command in that tab/window related to the context.
597    /// The `Context::find()` can also be invoked to find a context.
598    /// # Arguments
599    /// * `network`  - The network (e.g. "freenode") of the context.
600    /// * `channel`  - The channel name for the context (e.g. "##rust").
601    /// # Returns
602    /// *  the context was found, i.e. if the user is joined to the channel
603    ///    specified currently, a `Some(<Context>)` is returned with the
604    ///    context object; `None` otherwise.
605    ///
606    pub fn find_context(&self, network: &str, channel: &str)
607        -> Option<Context>
608    {
609        Context::find(network, channel)
610    }
611
612    /// Returns a `Context` object for the current context (Hexchat tab/window
613    /// currently visible in the app). This object can be used to invoke
614    /// the Hexchat API within the context the object is bound to. Also,
615    /// `Context::get()` will return a context object for the current context.
616    /// # Returns
617    /// * The `Context` for the currently active context. This usually means
618    ///   the channel window the user has visible in the GUI.
619    ///
620    pub fn get_context(&self) -> Option<Context> {
621        Context::get()
622    }
623
624    /// Retrieves the info data with the given `id`. It returns None on failure
625    /// and `Some(String)` on success. All information is returned as String
626    /// data - even the "win_ptr"/"gtkwin_ptr" values, which can be parsed
627    /// and cast to pointers.
628    /// # Arguments
629    /// * `id` - The name/identifier for the information needed. A list of
630    ///          the names for some of these can be found on the Hexchat
631    ///          Plugin Interface page under `hexchat_get_info()`. These include
632    ///          "channel", "network", "topic", etc.
633    /// # Returns
634    /// * `Some(<String>)` is returned with the string value of the info
635    ///   requested. `None` is returned if there is no info with the requested
636    ///   `id`.
637    ///
638    pub fn get_info(&self, id: &str) -> Option<String> {
639        let idstr = str2cstring(id);
640        let info  = unsafe { (self.c_get_info)(self, idstr.as_ptr()) };
641        if !info.is_null() {
642            match id {
643                "win_ptr"  | "gtkwin_ptr"  => {
644                    Some((info as u64).to_string())
645                },
646                _ => {
647                    Some(pchar2string(info))
648                },
649            }
650        } else { None }
651    }
652
653    /// Returns the requested pref value, or None if it doesn't exist. These
654    /// are settings specific to Hexchat itself. It's possible to get the
655    /// user's input box text cursor position via this command with
656    /// "state_cursor", for instance. Other preferences can be listed with the
657    /// `/set` command.
658    /// # Arguments
659    /// * name - The name of the pref to read.
660    /// # Returns
661    /// * `Some(PrefValue)` if the pref exists, `None` otherwise.
662    ///
663    pub fn get_prefs(&self, name: &str) -> Option<PrefValue> {
664        let namecstr = str2cstring(name);
665        unsafe {
666            let mut str_ptr: *const c_char = ptr::null();
667            let mut int_loc: c_int = 0;
668            let result = (self.c_get_prefs)(self,
669                                            namecstr.as_ptr(),
670                                            &mut str_ptr,
671                                            &mut int_loc);
672            match result {
673                1 => { Some(StringVal(pchar2string(str_ptr))) },
674                2 => { Some(IntegerVal(int_loc as i32)) },
675                3 => { Some(BoolVal( int_loc != 0 )) },
676                _ => { None },
677            }
678        }
679    }
680
681    /// Creates an iterator for the requested Hexchat list. This is modeled
682    /// after how Hexchat implements the listing feature: rather than load
683    /// all the list items up front, an internal list pointer is advanced
684    /// to the current item, and the fields of which are accessible through
685    /// the iterator's `.get_field()` function. List iterators can also be
686    /// created by invoking `ListIterator::new(<list-name>)`. See the Hexchat
687    /// Plugin Interface web page for more information on the related lists.
688    /// # Arguments
689    /// * `name` - The name of the list to iterate over.
690    /// # Returns
691    /// * If the list exists, `Some(ListIterator)` is returned; `None`
692    ///   otherwise.
693    ///
694    pub fn list_get(&self, name: &str) -> Option<ListIterator> {
695        ListIterator::new(name)
696    }
697
698    /// Writes a variable name and value to a configuration file maintained
699    /// by Hexchat for your plugin. These can be accessed later using
700    /// `pluginpref_get()`. *A character representing the type of the pref is
701    /// prepended to the value output to the config file. `pluginpref_get()`
702    /// uses this when reading back values from the config file to return the
703    /// correct variant of `PrefValue`.*
704    /// # Arguments
705    /// * `name`    - The name of the pref to set.
706    /// * `value`   - The value to set - an instance of one of the `PrefValue`
707    ///               types (`StringVal, IntVal, or BoolVal`).
708    /// # Returns
709    /// * `true` if the operation succeeds, `false` otherwise.
710    ///
711    pub fn pluginpref_set(&self, name: &str, value: PrefValue) -> bool {
712        let sval = value.simple_ser();
713        let namecstr = str2cstring(name);
714        let svalcstr = CString::new(sval.clone()).unwrap();
715        if sval.len() > MAX_PREF_VALUE_SIZE {
716            panic!("`hexchat.pluginpref_set({}, <overflow>)`: the value \
717                    exceeds the max allowable size of {:?} bytes.",
718                   name, MAX_PREF_VALUE_SIZE);
719        }
720        unsafe {
721            (self.c_pluginpref_set_str)(self,
722                                        namecstr.as_ptr(),
723                                        svalcstr.as_ptr()) > 0
724        }
725    }
726
727    /// Retrieves, from a config file that Hexchat manages for your plugin,
728    /// the value for the named variable that had been previously created using
729    /// `pluginpref_set()`.
730    /// # Arguments
731    /// * `name` - The name of the pref to load.
732    /// # Returns
733    /// * `Some(<PrefValue>)` holding the value of the requested pref if it
734    ///   exists, `None` otherwise.
735    ///
736    pub fn pluginpref_get(&self, name: &str) -> Option<PrefValue> {
737        let mut buf = [0i8; MAX_PREF_VALUE_SIZE];
738        let name = str2cstring(name);
739        if unsafe { (self.c_pluginpref_get_str)(self,
740                                                name.as_ptr(),
741                                                buf.as_mut_ptr()) > 0 }
742        {
743            let sval = pchar2string(buf.as_ptr());
744            Some(PrefValue::simple_deser(&sval))
745        } else { None }
746    }
747
748    /// Returns a list of all the plugin pref variable names your plugin
749    /// registered using `pluginpref_set()`. `pluginpref_get()` can be invoked
750    /// with each item to get their values.
751    /// # Returns
752    /// * `Some(Vec<String>)` if prefs exist for the plugin, `None` otherwise.
753    ///    The vector contains the names of the prefs registered.
754    ///
755    pub fn pluginpref_list(&self) -> Option<Vec<String>> {
756        let mut buf = [0i8; MAX_PREF_LIST_SIZE];
757        if unsafe { (self.c_pluginpref_list)(self, buf.as_mut_ptr()) > 0 } {
758            let s = pchar2string(buf.as_ptr());
759            if !s.is_empty() {
760                let mut v = vec![];
761                for name in s.split(',') {
762                    if !name.is_empty() {
763                        v.push(name.to_string());
764                    }
765                }
766                Some(v)
767            } else { None }
768        } else { None }
769    }
770
771    /// Adds a dummy entry in Hexchat's list of plugins. The "plugin" registered
772    /// using this command is visible in the "Plugins and Scripts" dialog and
773    /// using other slash "/" commands; however, that's all it does. This
774    /// command is useful when embedding a script interpreter that loads
775    /// scripts as plugin code. Each script thus loaded can be visible to the
776    /// user as a plugin. If writing a native plugin, you don't need to be
777    /// concerned with this command as your plugin's info is registered during
778    /// init from the `PluginInfo` object provided by your `plugin_get_info()`
779    /// function.
780    /// # Arguments
781    /// * `filename`    - This can be the name of a script or binary.
782    /// * `name`        - The name of the plugin.
783    /// * `desc`        - The description of the plugin.
784    /// * `version`     - A version string.
785    /// # Returns
786    /// * A new `Plugin` object that represents the plugin entry in Hexchat.
787    ///   It can be used to deregister the plugin, and it (or a clone of it)
788    ///   needs to be retained; otherwise, the plugin entry will be removed
789    ///   when the last copy of the `Plugin` object goes out of scope.
790    ///
791    pub fn plugingui_add(&self,
792                         filename : &str,
793                         name     : &str,
794                         desc     : &str,
795                         version  : &str)
796        -> Plugin
797    {
798        Plugin::new(filename, name, desc, version)
799    }
800
801    /// Removes the dummy plugin entry from the Hexchat environment. The
802    /// dummy plugin would have been registered using `hexchat.plugingui_add()`.
803    ///
804    pub fn plugingui_remove(&self, plugin: &Plugin) {
805        plugin.remove();
806    }
807}
808
809/// Represents the values that can be accessed using the prefs functions of
810/// the `Hexchat` object (`hc.pluginpref_get()`, `hc.pluginpref_get()`, etc.).
811/// The enumeration enables the typing of the values stored and retrieved.
812///
813#[derive(Debug)]
814pub enum PrefValue {
815    StringVal(String),
816    IntegerVal(i32),
817    BoolVal(bool),
818}
819use PrefValue::*;
820
821impl PrefValue {
822    pub fn str(&self) -> String {
823        match self {
824            StringVal(s) => { s.clone() },
825            IntegerVal(i) => { i.to_string() },
826            BoolVal(b) => { b.to_string() },
827        }
828    }
829    pub fn int(&self) -> i32 {
830        match self {
831            StringVal(s) => {
832                s.parse::<i32>().unwrap_or(0)
833            },
834            IntegerVal(i) => { *i },
835            BoolVal(b) => { if *b { 1 } else { 0 } },
836        }
837    }
838
839    pub fn bool(&self) -> bool {
840        match self {
841            StringVal(s) => {
842                s.parse::<bool>().unwrap_or(false)
843            },
844            IntegerVal(i) => { *i != 0 },
845            BoolVal(b) => { *b },
846        }
847    }
848
849    /// Simple config file value serialization into string.
850    /// The string produced can be written to the config file Hexchat maintains.
851    /// A type character is prepended ('s', 'i', or 'b').
852    ///
853    fn simple_ser(&self) -> String {
854        match self {
855            StringVal(s) => {
856                let mut sstr = s.clone();
857                sstr.insert(0, 's');
858                sstr
859            },
860            IntegerVal(i) => {
861                let mut istr = i.to_string();
862                istr.insert(0, 'i');
863                istr
864            },
865            BoolVal(b) => {
866                let mut bstr = b.to_string();
867                bstr.insert(0, 'b');
868                bstr
869            },
870        }
871    }
872    /// Simple config file value deserialization from a string to a `PrefValue`.
873    /// Treats the first character of the string read in from the config file
874    /// as the type, which it then discards and parses the rest of the string
875    /// to return the correct variant of `PrefValue`.
876    ///
877    fn simple_deser(s: &str) -> PrefValue {
878        if s.len() > 1 {
879            match &s[0..1] {
880                "s" => {
881                    StringVal(s.to_string())
882                },
883                "i" => {
884                    if let Ok(v) = s[1..].parse::<i32>() {
885                        IntegerVal(v)
886                    } else { StringVal(s.to_string()) }
887                },
888                "b" => {
889                    if let Ok(v) = s[1..].parse::<bool>() {
890                        BoolVal(v)
891                    } else { StringVal(s.to_string()) }
892                },
893                _ => { StringVal(s.to_string()) },
894            }
895        } else {
896            StringVal(s.to_string())
897        }
898    }
899}
900
901impl From<PrefValue> for String {
902    fn from(pv: PrefValue) -> String {
903        pv.str()
904    }
905}
906
907impl From<PrefValue> for i32 {
908    fn from(pv: PrefValue) -> i32 {
909        pv.int()
910    }
911}
912
913impl From<PrefValue> for bool {
914    fn from(pv: PrefValue) -> bool {
915        pv.bool()
916    }
917}
918
919impl From<String> for PrefValue {
920    fn from(s: String) -> PrefValue {
921        StringVal(s)
922    }
923}
924
925impl From<i32> for PrefValue {
926    fn from(i: i32) -> PrefValue {
927        IntegerVal(i)
928    }
929}
930
931impl From<bool> for PrefValue {
932    fn from(b: bool) -> PrefValue {
933        BoolVal(b)
934    }
935}
936
937// Apply the same attribute to all items in the block, but don't compile it as
938// an actual block.
939macro_rules! apply_attrib {
940    (#![$attr:meta] $( $it:item )*) => { $( #[$attr] $it )* }
941}
942
943apply_attrib! {
944#![allow(non_camel_case_types)]
945
946/// Some types used by the C struct below.
947pub (crate) type hexchat_hook        = c_void;
948pub (crate) type hexchat_list        = c_void;
949pub (crate) type hexchat_context     = c_void;
950#[allow(dead_code)]
951pub (crate) type hexchat_event_attrs = c_void;
952
953/// Mirrors the callback function pointer of Hexchat.
954type C_Callback      = extern "C"
955                       fn(word       : *const *const c_char,
956                          word_eol   : *const *const c_char,
957                          user_data  : *mut c_void
958                         ) -> c_int;
959
960/// Mirrors the print callback function pointer of Hexchat.
961type C_PrintCallback = extern "C"
962                       fn(word       : *const *const c_char,
963                          user_data  : *mut c_void
964                         ) -> c_int;
965
966/// Mirrors the timer callback function pointer of Hexchat.
967type C_TimerCallback = extern "C"
968                       fn(user_data  : *mut c_void
969                         ) -> c_int;
970
971/// Mirrors the print attr callback function pointer of Hexchat.
972type C_AttrCallback  = extern "C"
973                       fn(word       : *const *const c_char,
974                          attrs      : *const EventAttrs,
975                          user_data  : *mut c_void
976                         ) -> c_int;
977
978/// Mirrors the FD related callback function pointer of Hexchat.
979type C_FDCallback    = extern "C"
980                       fn(fd         : c_int,
981                          flags      : c_int,
982                          udata      : *mut c_void
983                         ) -> c_int;
984}
985
986/// Mirrors the C struct for `hexchat_event_attrs`. It holds the timestamps
987/// for the callback invocations for callbacks registered using
988/// `hexchat_print_attrs()`, and similar commands.
989#[repr(C)]
990pub struct EventAttrs {
991    pub server_time_utc : time_t
992}
993
994impl fmt::Debug for Hexchat {
995    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996        f.debug_struct("Hexchat")
997            .field("raw_function_pointers", &"...")
998            .finish()
999    }
1000}
1001
1002/// This struct mirrors the C Hexchat struct passed to the plugin from
1003/// Hexchat when the plugin is loaded. Hexchat's API is implemented as a struct
1004/// holding callbacks to its native functions. *Don't modify* this struct,
1005/// unless there has been a change to the layout of it in the Hexchat C code
1006/// base.
1007#[repr(C)]
1008pub struct Hexchat {
1009    pub (crate)
1010    c_hook_command       : unsafe extern "C"
1011                           fn(hp     : *const Hexchat,
1012                              name   : *const c_char,
1013                              pri    : c_int,
1014                              cb     : C_Callback,
1015                              help   : *const c_char,
1016                              udata  : *mut c_void
1017                             ) -> *const hexchat_hook,
1018    pub (crate)
1019    c_hook_server        : unsafe extern "C"
1020                           fn(hp     : *const Hexchat,
1021                              name   : *const c_char,
1022                              pri    : c_int,
1023                              cb     : C_Callback,
1024                              udata  : *mut c_void
1025                             ) -> *const hexchat_hook,
1026    pub (crate)
1027    c_hook_print         : unsafe extern "C"
1028                           fn(hp     : *const Hexchat,
1029                              name   : *const c_char,
1030                              pri    : c_int,
1031                              cb     : C_PrintCallback,
1032                              udata  : *mut c_void
1033                             ) -> *const hexchat_hook,
1034    pub (crate)
1035    c_hook_timer         : unsafe extern "C"
1036                           fn(hp     : *const Hexchat,
1037                              timeout: c_int,
1038                              cb     : C_TimerCallback,
1039                              udata  : *mut c_void
1040                             ) -> *const hexchat_hook,
1041    pub (crate)
1042    c_hook_fd            : unsafe extern "C"
1043                           fn(hp     : *const Hexchat,
1044                              fd     : c_int,
1045                              flags  : c_int,
1046                              cb     : C_FDCallback,
1047                              udata  : *mut c_void
1048                             ) -> *const hexchat_hook,
1049    pub (crate)
1050    c_unhook             : unsafe extern "C"
1051                           fn(hp     : *const Hexchat,
1052                              hook   : *const hexchat_hook,
1053                             ) -> *const c_void,
1054    pub (crate)
1055    c_print              : unsafe extern "C"
1056                           fn(hp     : *const Hexchat,
1057                              text   : *const c_char),
1058    pub (crate)
1059    c_printf             : unsafe extern "C"
1060                           fn(hp     : *const Hexchat,
1061                              text   : *const c_char,
1062                              ...),
1063    pub (crate)
1064    c_command            : unsafe extern "C"
1065                           fn(hp     : *const Hexchat,
1066                              command: *const c_char),
1067    pub (crate)
1068    c_commandf           : unsafe extern "C"
1069                           fn(hp     : *const Hexchat,
1070                              command: *const c_char,
1071                              ...),
1072    pub (crate)
1073    c_nickcmp            : unsafe extern "C"
1074                           fn(hp     : *const Hexchat,
1075                              s1     : *const c_char,
1076                              s2     : *const c_char
1077                             ) -> c_int,
1078    pub (crate)
1079    c_set_context        : unsafe extern "C"
1080                           fn(hp     : *const Hexchat,
1081                              ctx    : *const hexchat_context
1082                             ) -> c_int,
1083    pub (crate)
1084    c_find_context       : unsafe extern "C"
1085                           fn(hp         : *const Hexchat,
1086                              srv_name   : *const c_char,
1087                              channel    : *const c_char,
1088                             ) -> *const hexchat_context,
1089    pub (crate)
1090    c_get_context        : unsafe extern "C"
1091                           fn(hp: *const Hexchat
1092                             ) -> *const hexchat_context,
1093    pub (crate)
1094    c_get_info           : unsafe extern "C"
1095                           fn(hp     : *const Hexchat,
1096                              id     : *const c_char,
1097                             ) -> *const c_char,
1098    pub (crate)
1099    c_get_prefs          : unsafe extern "C"
1100                           fn(hp     : *const Hexchat,
1101                              name   : *const c_char,
1102                              string : *mut *const c_char,
1103                              integer: *mut c_int
1104                             ) -> c_int,
1105    pub (crate)
1106    c_list_get           : unsafe extern "C"
1107                           fn(hp     : *const Hexchat,
1108                              name   : *const c_char
1109                             ) -> *const hexchat_list,
1110    pub (crate)
1111    c_list_free          : unsafe extern "C"
1112                           fn(hp     : *const Hexchat,
1113                              hclist : *const hexchat_list
1114                             ),
1115    pub (crate)
1116    c_list_fields        : unsafe extern "C"
1117                           fn(hp     : *const Hexchat,
1118                              name   : *const c_char
1119                             ) -> *const *const c_char,
1120    pub (crate)
1121    c_list_next          : unsafe extern "C"
1122                           fn(hp     : *const Hexchat,
1123                              hclist : *const hexchat_list
1124                             ) -> c_int,
1125    pub (crate)
1126    c_list_str           : unsafe extern "C"
1127                           fn(hp     : *const Hexchat,
1128                              hclist : *const hexchat_list,
1129                              field  : *const c_char
1130                             ) -> *const c_char,
1131    pub (crate)
1132    c_list_int           : unsafe extern "C"
1133                           fn(hp     : *const Hexchat,
1134                              hclist : *const hexchat_list,
1135                              field  : *const c_char
1136                             ) -> c_int,
1137    pub (crate)
1138    c_plugingui_add      : unsafe extern "C"
1139                           fn(hp         : *const Hexchat,
1140                              filename   : *const c_char,
1141                              name       : *const c_char,
1142                              desc       : *const c_char,
1143                              version    : *const c_char,
1144                              reserved   : *const c_char
1145                             ) -> *const c_void,
1146    pub (crate)
1147    c_plugingui_remove   : unsafe extern "C"
1148                           fn(hp: *const Hexchat, handle: *const c_void),
1149    pub (crate)
1150    c_emit_print         : unsafe extern "C"
1151                           fn(hp         : *const Hexchat,
1152                              event_name : *const c_char,
1153                              ...
1154                             ) -> c_int,
1155    pub (crate)
1156    c_read_fd            : unsafe extern "C"
1157                           fn(hp     : *const Hexchat,
1158                              src    : *const c_void,
1159                              buf    : *mut c_char,
1160                              len    : *mut c_int
1161                              ) -> c_int,
1162    pub (crate)
1163    c_list_time          : unsafe extern "C"
1164                           fn(hp     : *const Hexchat,
1165                              hclist : *const hexchat_list,
1166                              name   : *const c_char
1167                             ) -> time_t,
1168    pub (crate)
1169    c_gettext            : unsafe extern "C"
1170                           fn(hp     : *const Hexchat,
1171                              msgid  : *const c_char
1172                             ) -> *const c_char,
1173    pub (crate)
1174    c_send_modes         : unsafe extern "C"
1175                           fn(hp             : *const Hexchat,
1176                              targets        : *const *const c_char,
1177                              n_targets      : c_int,
1178                              modes_per_line : c_int,
1179                              sign           : c_char,
1180                              mode           : c_char
1181                             ),
1182    pub (crate)
1183    c_strip              : unsafe extern "C"
1184                           fn(hp     : *const Hexchat,
1185                              string : *const c_char,
1186                              len    : c_int,
1187                              flags  : c_int
1188                             ) -> *const c_char,
1189    pub (crate)
1190    c_free               : unsafe extern "C"
1191                           fn(hp     : *const Hexchat,
1192                              ptr    : *const c_void,
1193                             ),
1194    pub (crate)
1195    c_pluginpref_set_str : unsafe extern "C"
1196                           fn(hp     : *const Hexchat,
1197                              var    : *const c_char,
1198                              value  : *const c_char
1199                             ) -> c_int,
1200    pub (crate)
1201    c_pluginpref_get_str : unsafe extern "C"
1202                           fn(hp     : *const Hexchat,
1203                              var    : *const c_char,
1204                              dest   : *mut c_char
1205                             ) -> c_int,
1206    pub (crate)
1207    c_pluginpref_set_int : unsafe extern "C"
1208                           fn(hp     : *const Hexchat,
1209                              var    : *const c_char,
1210                              value  : c_int
1211                             ) -> c_int,
1212    pub (crate)
1213    c_pluginpref_get_int : unsafe extern "C"
1214                           fn(hp     : *const Hexchat,
1215                              var    : *const c_char
1216                             ) -> c_int,
1217    pub (crate)
1218    c_pluginpref_delete  : unsafe extern "C"
1219                           fn(hp     : *const Hexchat,
1220                              var    : *const c_char
1221                             ) -> c_int,
1222    pub (crate)
1223    c_pluginpref_list    : unsafe extern "C"
1224                           fn(hp     : *const Hexchat,
1225                              dest   : *mut c_char
1226                             ) -> c_int,
1227    pub (crate)
1228    c_hook_server_attrs  : unsafe extern "C"
1229                           fn(hp     : *const Hexchat,
1230                              name   : *const c_char,
1231                              pri    : c_int,
1232                              cb     : C_AttrCallback,
1233                              udata  : *const c_void
1234                             ) -> *const hexchat_hook,
1235    pub (crate)
1236    c_hook_print_attrs   : unsafe extern "C"
1237                           fn(hp     : *const Hexchat,
1238                              name   : *const c_char,
1239                              pri    : c_int,
1240                              cb     : C_AttrCallback,
1241                              udata  : *const c_void
1242                             ) -> *const hexchat_hook,
1243    pub (crate)
1244    c_emit_print_attrs   : unsafe extern "C"
1245                           fn(hp         : *const Hexchat,
1246                              attrs      : *const EventAttrs,
1247                              event_name : *const c_char,
1248                              ...
1249                             ) -> c_int,
1250    pub (crate)
1251    c_event_attrs_create : unsafe extern "C"
1252                           fn(hp: *const Hexchat) -> *mut EventAttrs,
1253    pub (crate)
1254    c_event_attrs_free   : unsafe extern "C"
1255                           fn(hp     : *const Hexchat,
1256                              attrs  : *mut EventAttrs
1257                             ),
1258}