hexchat_api/
threadsafe_hexchat.rs

1#![cfg(feature = "threadsafe")]
2
3//! A thread-safe wrapper for the `Hexchat` object. The methods of the object
4//! will execute on the Hexchat main thread when called from another thread.
5//! The client code doesn't have to worry about synchronization; that's taken
6//! care of internally by `ThreadSafeHexchat`.
7
8use crate::HexchatError;
9use crate::hexchat::*;
10use crate::thread_facilities::*;
11use crate::threadsafe_context::*;
12use crate::threadsafe_list_iterator::*;
13
14use HexchatError::*;
15
16/// A thread-safe wrapper for the `Hexchat` object.
17/// It implements a subset of the methods provided by the wrapped object.
18/// A lot of methods don't make sense to expose; those that do should provide
19/// enough utility to code that needs to invoke Hexchat from other threads.
20///
21#[derive(Clone, Copy)]
22pub struct ThreadSafeHexchat;
23
24unsafe impl Send for ThreadSafeHexchat {}
25unsafe impl Sync for ThreadSafeHexchat {}
26
27impl ThreadSafeHexchat {
28    /// Constructs a `ThreadSafeHexchat` object that wraps `Hexchat`. Only to
29    /// be called from the main thread internally.
30    /// 
31    pub (crate)
32    fn new(_hc: &'static Hexchat) -> Self {
33        ThreadSafeHexchat
34    }
35
36    /// Prints the string passed to it to the active Hexchat window.
37    /// # Arguments
38    /// * `text` - The text to print.
39    ///
40    pub fn print(&self, text: &str) -> Result<(), HexchatError> {
41        let text = text.to_string();
42        let result = main_thread(move |hc| hc.print(&text));
43        result.get()
44    }
45
46    /// Invokes the Hexchat command specified by `command`.
47    /// # Arguments
48    /// * `command` - The Hexchat command to invoke.
49    ///
50    pub fn command(&self, command: &str) -> Result<(), HexchatError> {
51        let command = command.to_string();
52        let result = main_thread(move |hc| hc.command(&command));
53        result.get()
54    }
55
56    /// Returns a `ThreadSafeContext` object bound to the requested channel.
57    /// The object provides methods like `print()` that will execute the
58    /// Hexchat print command in that tab/window related to the context.
59    /// The `Context::find()` can also be invoked to find a context.
60    /// # Arguments
61    /// * `network`  - The network (e.g. "freenode") of the context.
62    /// * `channel`  - The channel name for the context (e.g. "##rust").
63    /// # Returns
64    /// * the thread-safe context was found, i.e. if the user is joined to the
65    ///   channel specified currently, the context or a `HexchatError` if the
66    ///   context wasn't found, or another problem occurred..
67    ///
68    pub fn find_context(&self,
69                        network : &str,
70                        channel : &str)
71        -> Result<ThreadSafeContext, HexchatError>
72    {
73        let data = (network.to_string(), channel.to_string());
74        main_thread(move |hc| {
75            hc.find_context(&data.0, &data.1).map(ThreadSafeContext::new)
76        }).get().and_then(|r| 
77            r.ok_or_else(||{ 
78                let msg = format!("{}, {}", network, channel); 
79                ContextAcquisitionFailed(msg) 
80            }))
81    }
82
83    /// This should be invoked from the main thread. The context object returned
84    /// can then be moved to a thread that uses it. Executing this from a
85    /// separate thread may not grab the expected context internally.
86    ///
87    /// Returns a `ThreadSafeContext` object for the current context
88    /// currently visible in the app). This object can be used to invoke
89    /// the Hexchat API within the context the object is bound to. Also,
90    /// `Context::get()` will return a context object for the current context.
91    /// # Returns
92    /// * The `ThreadSafeContext` for the currently active context. This usually
93    ///   means the channel window the user has visible in the GUI.
94    ///
95    pub fn get_context(&self) -> Result<ThreadSafeContext, HexchatError> {
96        main_thread(|hc| {
97            hc.get_context().map(ThreadSafeContext::new)
98        })
99        .get()
100        .and_then(|r| r.ok_or_else(|| ContextAcquisitionFailed("?, ?".into())))
101    }
102
103    /// Retrieves the info data with the given `id`. It returns None on failure
104    /// and `Some(String)` on success. All information is returned as String
105    /// data - even the "win_ptr"/"gtkwin_ptr" values, which can be parsed
106    /// and cast to pointers.
107    /// # Arguments
108    /// * `id` - The name/identifier for the information needed. A list of
109    ///          the names for some of these can be found on the Hexchat
110    ///          Plugin Interface page under `hexchat_get_info()`. These include
111    ///          "channel", "network", "topic", etc.
112    /// # Returns
113    /// * The string is returned for the info requested. `HexchatError` is 
114    ///   returned if there is no info with the requested `id` or another 
115    ///   problem occurred.
116    ///
117    pub fn get_info(&self, id: &str) -> Result<String, HexchatError> {
118        let sid = id.to_string();
119        main_thread(move |hc| {
120            hc.get_info(&sid)
121        }).get().and_then(|r| r.ok_or_else(|| InfoNotFound(id.into())))
122    }
123
124    /// Creates an iterator for the requested Hexchat list. This is modeled
125    /// after how Hexchat implements the listing feature: rather than load
126    /// all the list items up front, an internal list pointer is advanced
127    /// to the current item, and the fields of which are accessible through
128    /// the iterator's `.get_field()` function.
129    /// See the Hexchat Plugin Interface web page for more information on the
130    /// related lists.
131    /// # Arguments
132    /// * `name` - The name of the list to iterate over.
133    /// # Returns
134    /// * If the list exists, a `ThreadSafeListIterator` is returned otherwise,
135    ///   an error is returned.
136    ///
137    pub fn list_get(&self, list: &str) 
138        -> Result<ThreadSafeListIterator, HexchatError> 
139    {
140        let slist = list.to_string();
141        main_thread(move |hc| {
142            hc.list_get(&slist).map(ThreadSafeListIterator::create)
143        }).get().and_then(|r| r.ok_or_else(|| ListNotFound(list.into())))
144    }
145}
146