use std::error;
use std::fmt;
use std::ffi::CString;
use std::rc::Rc;
#[cfg(feature = "threadsafe")]
use std::thread;
#[cfg(feature = "threadsafe")]
use crate::MAIN_THREAD_ID;
use crate::hexchat::{Hexchat, hexchat_context, HexchatError};
use crate::hexchat_entry_points::PHEXCHAT;
use crate::list_iterator::ListIterator;
use crate::utils::*;
use ContextError::*;
use HexchatError::*;
#[derive(Debug)]
struct ContextData {
hc : &'static Hexchat,
network : CString,
channel : CString,
}
#[derive(Clone)]
pub struct Context {
data : Rc<ContextData>,
}
impl Context {
pub fn find(network: &str, channel: &str) -> Option<Self> {
#[cfg(feature = "threadsafe")]
assert!(thread::current().id() == unsafe { MAIN_THREAD_ID.unwrap() },
"Context::find() must be called from the Hexchat main thread.");
let csnetwork = str2cstring(network);
let cschannel = str2cstring(channel);
let hc = unsafe { &*PHEXCHAT };
let context_ptr;
unsafe {
context_ptr = (hc.c_find_context)(hc,
csnetwork.as_ptr(),
cschannel.as_ptr());
}
if !context_ptr.is_null() {
let ctx = Context {
data: Rc::new(
ContextData {
hc,
network : csnetwork,
channel : cschannel,
})};
Some(ctx)
} else {
None
}
}
pub fn get() -> Option<Self> {
#[cfg(feature = "threadsafe")]
assert!(thread::current().id() == unsafe { MAIN_THREAD_ID.unwrap() },
"Context::get() must be called from the Hexchat main thread.");
unsafe {
let hc = &*PHEXCHAT;
let ctx_ptr = (hc.c_get_context)(hc);
if !ctx_ptr.is_null() {
let nwstr = str2cstring("network");
let chstr = str2cstring("channel");
let network = (hc.c_get_info)(hc, nwstr.as_ptr());
let channel = (hc.c_get_info)(hc, chstr.as_ptr());
let ctx = Context {
data: Rc::new(
ContextData {
hc,
network : pchar2cstring(network),
channel : pchar2cstring(channel),
})
};
Some(ctx)
} else{
None
}
}
}
#[inline]
fn acquire(&self) -> Result<*const hexchat_context, ContextError> {
let data = &*self.data;
let ptr = unsafe {
(data.hc.c_find_context)(data.hc,
data.network.as_ptr(),
data.channel.as_ptr())
};
if !ptr.is_null() {
Ok(ptr)
} else {
Err(AcquisitionFailed(cstring2string(&data.network),
cstring2string(&data.channel)))
}
}
pub fn set(&self) -> Result<(), ContextError> {
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
if (data.hc.c_set_context)(data.hc, ptr) > 0 {
Ok(())
} else { Err(OperationFailed(".set() failed.".to_string())) }
}
}
pub fn print(&self, message: &str) -> Result<(), ContextError> {
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
let prior = (data.hc.c_get_context)(data.hc);
(data.hc.c_set_context)(data.hc, ptr);
data.hc.print(message);
(data.hc.c_set_context)(data.hc, prior);
Ok(())
}
}
pub fn emit_print(&self, event_name: &str, var_args: &[&str])
-> Result<(), ContextError>
{
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
let prior = (data.hc.c_get_context)(data.hc);
(data.hc.c_set_context)(data.hc, ptr);
let result = data.hc.emit_print(event_name, var_args);
(data.hc.c_set_context)(data.hc, prior);
if let Err(CommandFailed(msg)) = result {
Err(OperationFailed(msg))
} else {
Ok(())
}
}
}
pub fn command(&self, command: &str) -> Result<(), ContextError> {
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
let prior = (data.hc.c_get_context)(data.hc);
(data.hc.c_set_context)(data.hc, ptr);
data.hc.command(command);
(data.hc.c_set_context)(data.hc, prior);
Ok(())
}
}
pub fn get_info(&self, list: &str) -> Result<String, ContextError> {
use ContextError::*;
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
let prior = (data.hc.c_get_context)(data.hc);
(data.hc.c_set_context)(data.hc, ptr);
let result = data.hc.get_info(list);
(data.hc.c_set_context)(data.hc, prior);
result.ok_or_else(|| InfoNotFound(list.to_string()))
}
}
pub fn list_get(&self, list: &str)
-> Result<ListIterator, ContextError>
{
let data = &*self.data;
unsafe {
let ptr = self.acquire()?;
let prior = (data.hc.c_get_context)(data.hc);
(data.hc.c_set_context)(data.hc, ptr);
let iter = ListIterator::new(list);
(data.hc.c_set_context)(data.hc, prior);
iter.ok_or_else(|| ListNotFound(list.to_string()))
}
}
pub fn network(&self) -> String {
cstring2string(&self.data.network)
}
pub fn channel(&self) -> String {
cstring2string(&self.data.channel)
}
}
impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data = &*self.data;
let network = cstring2string(&data.network);
let channel = cstring2string(&data.channel);
write!(f, "Context(\"{}\", \"{}\")", network, channel)
}
}
#[derive(Debug, Clone)]
pub enum ContextError {
AcquisitionFailed(String, String),
OperationFailed(String),
ContextDropped(String),
ThreadSafeOperationFailed(String),
ListNotFound(String),
InfoNotFound(String),
}
impl error::Error for ContextError {}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = format!("{:?}", self);
s.retain(|c| c != '"');
write!(f, "{}", s)
}
}