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