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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
#![cfg(feature = "threadsafe")]
//! A thread-safe wrapper for the `Hexchat` object. The methods of the object
//! will execute on the Hexchat main thread when called from another thread.
//! The client code doesn't have to worry about synchronization; that's taken
//! care of internally by `ThreadSafeHexchat`.
use crate::HexchatError;
use crate::hexchat::*;
use crate::thread_facilities::*;
use crate::threadsafe_context::*;
use crate::threadsafe_list_iterator::*;
use HexchatError::*;
/// A thread-safe wrapper for the `Hexchat` object.
/// It implements a subset of the methods provided by the wrapped object.
/// A lot of methods don't make sense to expose; those that do should provide
/// enough utility to code that needs to invoke Hexchat from other threads.
///
#[derive(Clone, Copy)]
pub struct ThreadSafeHexchat;
unsafe impl Send for ThreadSafeHexchat {}
unsafe impl Sync for ThreadSafeHexchat {}
impl ThreadSafeHexchat {
/// Constructs a `ThreadSafeHexchat` object that wraps `Hexchat`. Only to
/// be called from the main thread internally.
///
pub (crate)
fn new(_hc: &'static Hexchat) -> Self {
ThreadSafeHexchat
}
/// Prints the string passed to it to the active Hexchat window.
/// # Arguments
/// * `text` - The text to print.
///
pub fn print(&self, text: &str) -> Result<(), HexchatError> {
let text = text.to_string();
let result = main_thread(move |hc| hc.print(&text));
result.get()
}
/// Invokes the Hexchat command specified by `command`.
/// # Arguments
/// * `command` - The Hexchat command to invoke.
///
pub fn command(&self, command: &str) -> Result<(), HexchatError> {
let command = command.to_string();
let result = main_thread(move |hc| hc.command(&command));
result.get()
}
/// Returns a `ThreadSafeContext` object bound to the requested channel.
/// The object provides methods like `print()` that will execute the
/// Hexchat print command in that tab/window related to the context.
/// The `Context::find()` can also be invoked to find a context.
/// # Arguments
/// * `network` - The network (e.g. "freenode") of the context.
/// * `channel` - The channel name for the context (e.g. "##rust").
/// # Returns
/// * the thread-safe context was found, i.e. if the user is joined to the
/// channel specified currently, the context or a `HexchatError` if the
/// context wasn't found, or another problem occurred..
///
pub fn find_context(&self,
network : &str,
channel : &str)
-> Result<ThreadSafeContext, HexchatError>
{
let data = (network.to_string(), channel.to_string());
main_thread(move |hc| {
hc.find_context(&data.0, &data.1).map(ThreadSafeContext::new)
}).get().and_then(|r|
r.ok_or_else(||{
let msg = format!("{}, {}", network, channel);
ContextAcquisitionFailed(msg)
}))
}
/// This should be invoked from the main thread. The context object returned
/// can then be moved to a thread that uses it. Executing this from a
/// separate thread may not grab the expected context internally.
///
/// Returns a `ThreadSafeContext` object for the current context
/// currently visible in the app). This object can be used to invoke
/// the Hexchat API within the context the object is bound to. Also,
/// `Context::get()` will return a context object for the current context.
/// # Returns
/// * The `ThreadSafeContext` for the currently active context. This usually
/// means the channel window the user has visible in the GUI.
///
pub fn get_context(&self) -> Result<ThreadSafeContext, HexchatError> {
main_thread(|hc| {
hc.get_context().map(ThreadSafeContext::new)
})
.get()
.and_then(|r| r.ok_or_else(|| ContextAcquisitionFailed("?, ?".into())))
}
/// Retrieves the info data with the given `id`. It returns None on failure
/// and `Some(String)` on success. All information is returned as String
/// data - even the "win_ptr"/"gtkwin_ptr" values, which can be parsed
/// and cast to pointers.
/// # Arguments
/// * `id` - The name/identifier for the information needed. A list of
/// the names for some of these can be found on the Hexchat
/// Plugin Interface page under `hexchat_get_info()`. These include
/// "channel", "network", "topic", etc.
/// # Returns
/// * The string is returned for the info requested. `HexchatError` is
/// returned if there is no info with the requested `id` or another
/// problem occurred.
///
pub fn get_info(&self, id: &str) -> Result<String, HexchatError> {
let sid = id.to_string();
main_thread(move |hc| {
hc.get_info(&sid)
}).get().and_then(|r| r.ok_or_else(|| InfoNotFound(id.into())))
}
/// Creates an iterator for the requested Hexchat list. This is modeled
/// after how Hexchat implements the listing feature: rather than load
/// all the list items up front, an internal list pointer is advanced
/// to the current item, and the fields of which are accessible through
/// the iterator's `.get_field()` function.
/// See the Hexchat Plugin Interface web page for more information on the
/// related lists.
/// # Arguments
/// * `name` - The name of the list to iterate over.
/// # Returns
/// * If the list exists, a `ThreadSafeListIterator` is returned otherwise,
/// an error is returned.
///
pub fn list_get(&self, list: &str)
-> Result<ThreadSafeListIterator, HexchatError>
{
let slist = list.to_string();
main_thread(move |hc| {
hc.list_get(&slist).map(ThreadSafeListIterator::create)
}).get().and_then(|r| r.ok_or_else(|| ListNotFound(list.into())))
}
}