use core::ffi::c_ulong;
use core::ptr::NonNull;
use core::time::Duration;
use crate::{AllocContext, Connection, FFI, LogLevel, Logger};
#[derive(Debug)]
pub struct Context<'cb, 'cn> {
inner: NonNull<sys::xmpp_ctx_t>,
owned: bool,
connections: Vec<Connection<'cn, 'cb>>,
_logger_and_memory: Option<(Logger<'cb>, Box<sys::xmpp_mem_t>)>,
}
impl<'cb, 'cn> Context<'cb, 'cn> {
pub fn new(logger: Logger<'cb>) -> Self {
crate::init();
let memory = Box::new(AllocContext::get_xmpp_mem_t());
unsafe { Self::with_inner_owned(sys::xmpp_ctx_new(memory.as_ref(), logger.as_ptr()), memory, 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_owned(inner: *mut sys::xmpp_ctx_t, memory: Box<sys::xmpp_mem_t>, logger: Logger<'cb>) -> Self {
Self {
inner: NonNull::new(inner).expect("Cannot allocate memory for Context"),
owned: true,
connections: vec![],
_logger_and_memory: Some((logger, memory)),
}
}
#[inline]
unsafe fn with_inner_borrowed(inner: *mut sys::xmpp_ctx_t) -> Self {
Self {
inner: NonNull::new(inner).expect("Cannot allocate memory for Context"),
owned: false,
connections: vec![],
_logger_and_memory: None,
}
}
pub(crate) fn consume_connection(&mut self, conn: Connection<'cn, 'cb>) {
self.connections.push(conn);
}
pub unsafe fn from_ref(inner: *const sys::xmpp_ctx_t) -> Self {
unsafe { Self::from_ref_mut(inner.cast_mut()) }
}
pub unsafe fn from_ref_mut(inner: *mut sys::xmpp_ctx_t) -> Self {
unsafe { Self::with_inner_borrowed(inner) }
}
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(),
c_ulong::try_from(timeout.as_millis()).unwrap_or(c_ulong::MAX),
)
}
}
pub fn run_once(&mut self, timeout: Duration) {
unsafe {
sys::xmpp_run_once(
self.inner.as_ptr(),
c_ulong::try_from(timeout.as_millis()).unwrap_or(c_ulong::MAX),
)
}
}
pub fn run(&mut 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) {
#[repr(C)]
struct XmppCtxRepr {
mem: *const sys::xmpp_mem_t,
log: *const sys::xmpp_log_t,
}
let inner = unsafe { ctx.cast_mut().cast::<XmppCtxRepr>().as_ref() }.expect("Null pointer for Context");
if let Some(log) = unsafe { inner.log.as_ref() } {
if let Some(log_handler) = log.handler {
let area = FFI(area).send();
let msg = FFI(msg).send();
unsafe { log_handler(log.userdata, level, area.as_ptr(), msg.as_ptr()) }
}
}
}