use std::{
default::Default,
os::raw,
ptr::NonNull,
time::Duration,
};
use crate::{
AllocContext,
Connection,
FFI,
Logger,
LogLevel,
};
#[derive(Debug)]
pub struct Context<'lg, 'cn> {
inner: NonNull<sys::xmpp_ctx_t>,
owned: bool,
connections: Vec<Connection<'cn, 'lg>>,
_logger: Option<Logger<'lg>>,
_memory: Option<Box<sys::xmpp_mem_t>>,
}
impl<'lg, 'cn> Context<'lg, 'cn> {
pub fn new(logger: Logger<'lg>) -> Self {
crate::init();
let memory = Box::new(AllocContext::get_xmpp_mem_t());
unsafe {
Self::with_inner(
sys::xmpp_ctx_new(memory.as_ref(), logger.as_ptr()),
true,
Some(memory),
Some(logger),
)
}
}
pub fn new_with_default_logger() -> Context<'static, 'cn> {
Context::new(Logger::default())
}
pub fn new_with_null_logger() -> Context<'static, 'cn> {
Context::new(Logger::new_null())
}
#[inline]
unsafe fn with_inner(inner: *mut sys::xmpp_ctx_t, owned: bool, memory: Option<Box<sys::xmpp_mem_t>>, logger: Option<Logger<'lg>>) -> Self {
if owned && (memory.is_none() || logger.is_none()) {
panic!("Memory and logger must be supplied for owned Context instances");
}
Self {
inner: NonNull::new(inner).expect("Cannot allocate memory for Context"),
owned,
connections: Vec::with_capacity(0),
_memory: memory,
_logger: logger,
}
}
pub(crate) fn consume_connection(&mut self, conn: Connection<'cn, 'lg>) {
self.connections.push(conn);
}
pub unsafe fn from_ref(inner: *const sys::xmpp_ctx_t) -> Self {
Self::from_ref_mut(inner as _)
}
pub unsafe fn from_ref_mut(inner: *mut sys::xmpp_ctx_t) -> Self {
Self::with_inner(inner, false, None, None)
}
pub(crate) fn as_ptr(&self) -> *mut sys::xmpp_ctx_t { self.inner.as_ptr() }
pub fn set_timeout(&mut self, timeout: Duration) {
unsafe {
sys::xmpp_ctx_set_timeout(self.inner.as_mut(), timeout.as_millis() as raw::c_ulong)
}
}
pub fn run_once(&self, timeout: Duration) {
unsafe {
sys::xmpp_run_once(self.inner.as_ptr(), timeout.as_millis() as raw::c_ulong)
}
}
pub fn run(&self) {
unsafe {
sys::xmpp_run(self.inner.as_ptr())
}
}
pub fn stop(&self) {
unsafe {
sys::xmpp_stop(self.inner.as_ptr())
}
}
pub fn log(&self, level: LogLevel, area: &str, msg: &str) {
unsafe { ctx_log(self.inner.as_ptr(), level, area, msg) }
}
}
impl PartialEq for Context<'_, '_> {
fn eq(&self, other: &Context) -> bool {
self.inner == other.inner
}
}
impl Eq for Context<'_, '_> {}
impl Drop for Context<'_, '_> {
fn drop(&mut self) {
if self.owned {
self.connections.clear();
unsafe {
sys::xmpp_ctx_free(self.inner.as_mut());
}
}
}
}
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for Context<'_, '_> {}
pub(crate) unsafe fn ctx_log(ctx: *const sys::xmpp_ctx_t, level: sys::xmpp_log_level_t, area: &str, msg: &str) {
#[allow(non_camel_case_types)]
#[repr(C)]
struct _xmpp_ctx_t {
mem: *const sys::xmpp_mem_t,
log: *const sys::xmpp_log_t,
}
let inner = (ctx as *mut _xmpp_ctx_t).as_ref().expect("Null pointer for Context");
if let Some(log) = inner.log.as_ref() {
if let Some(log_handler) = log.handler {
let area = FFI(area).send();
let msg = FFI(msg).send();
log_handler(log.userdata, level, area.as_ptr(), msg.as_ptr());
}
}
}