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