1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::ffi::{CString, c_void};
use crate::{str2cstring, PHEXCHAT};
use std::ptr;
use std::cell::RefCell;
use std::rc::Rc;

// TODO - Currently there's no use for retaining all the data except for the
//        handle. Consider removing the CString fields.
#[allow(dead_code)]
struct PluginData {
    file_name   : CString,
    plugin_name : CString,
    description : CString,
    version     : CString,
    handle      : *const c_void,
    removed     : bool,
}

/// Represents a created plugin entry. Plugins that embed other language
/// interpreters and load plugins written in those languages can have Hexchat
/// look as if the loaded scripts are actual plugins. By creating a `Plugin`
/// object for such a script, an entry is made in Hexchat's list of loaded
/// plugins. When one of these scripts is unloaded, the fictitious plugin entry
/// can be removed from Hexchat by dropping the associated `Plugin` object.
///
#[derive(Clone)]
pub struct Plugin {
    data: Rc<RefCell<PluginData>>,
}

impl Plugin {
    /// Creates a new plugin entry in Hexchat.
    /// # Arguments
    /// `file_name`     - The name of the script representing a "plugin".
    /// `plugin_name`   - The name of the plugin script.
    /// `description`   - The plugin script's description.
    /// `version`       - A version string for the plugin script.
    ///
    pub fn new(file_name    : &str,
               plugin_name  : &str,
               description  : &str,
               version      : &str)
        -> Plugin
    {
        unsafe {
            let hc   = &*PHEXCHAT;
            let null = ptr::null::<c_void>();
            let file_name   = str2cstring(file_name);
            let plugin_name = str2cstring(plugin_name);
            let description = str2cstring(description);
            let version     = str2cstring(version);

            let handle = (hc.c_plugingui_add)(hc,
                                              file_name.as_ptr(),
                                              plugin_name.as_ptr(),
                                              description.as_ptr(),
                                              version.as_ptr(),
                                              null.cast());
            let pd = PluginData {
                file_name,
                plugin_name,
                description,
                version,
                handle      : handle.cast(),
                removed     : false,
            };
            Plugin { data: Rc::new(RefCell::new(pd)) }
        }
    }
    /// Removes the plugin entry for the plugin script. This can be used to
    /// remove a plugin entry, or simply dropping the `Plugin` object will
    /// cause removal to happen automatically.
    ///
    pub fn remove(&self) {
        let cell = &*self.data;
        let data = &mut *cell.borrow_mut();
        if !data.removed {
            unsafe {
                let hc = &*PHEXCHAT;
                (hc.c_plugingui_remove)(hc, data.handle.cast());
            }
            data.removed = true;
        }
    }
}

impl Drop for PluginData {
    /// Removes the entry in Hexchat's plugins list for the `Plugin`.
    fn drop(&mut self) {
        if !self.removed {
            unsafe {
                let hc = &*PHEXCHAT;
                (hc.c_plugingui_remove)(hc, self.handle.cast());
            }
        }
    }
}