use crate::error::{Error, Result};
use crate::identity::Identity;
use crate::log::Log;
use crate::server::{Reply, Request, Responder};
use core::ffi::c_void;
use nwep_sys as sys;
type AppendHook = Box<dyn FnMut(&[u8], u64) + Send>;
pub enum DispatchOutcome {
Handled(Reply),
NotMine(Responder),
}
pub struct LogServer {
raw: *mut sys::nwep_log_server,
hook: Option<*mut AppendHook>,
_log: Log,
}
unsafe impl Send for LogServer {}
impl LogServer {
pub fn new(identity: &Identity, log: Log) -> Result<LogServer> {
let raw =
identity.with_keypair(|kp| unsafe { sys::nwep_log_server_create(kp, log.as_ptr()) });
if raw.is_null() {
return Err(Error::InternalAlloc);
}
Ok(LogServer {
raw,
hook: None,
_log: log,
})
}
pub fn on_append(&mut self, hook: impl FnMut(&[u8], u64) + Send + 'static) {
self.clear_hook();
let boxed: *mut AppendHook = Box::into_raw(Box::new(Box::new(hook)));
unsafe {
sys::nwep_log_server_set_on_append(
self.raw,
Some(append_trampoline),
boxed as *mut c_void,
)
};
self.hook = Some(boxed);
}
pub fn dispatch(&self, req: &Request, res: Responder, now_secs: i64) -> DispatchOutcome {
let rc = unsafe {
sys::nwep_log_server_dispatch(
self.raw,
req.conn_id(),
req.raw_msg(),
res.raw_buf(),
now_secs,
)
};
if rc == 1 {
DispatchOutcome::NotMine(res)
} else {
DispatchOutcome::Handled(res.finish(rc))
}
}
pub fn as_ptr(&self) -> *mut sys::nwep_log_server {
self.raw
}
fn clear_hook(&mut self) {
if let Some(ptr) = self.hook.take() {
unsafe {
sys::nwep_log_server_set_on_append(self.raw, None, core::ptr::null_mut());
drop(Box::from_raw(ptr));
}
}
}
}
impl Drop for LogServer {
fn drop(&mut self) {
unsafe { sys::nwep_log_server_free(self.raw) };
self.clear_hook();
}
}
unsafe extern "C" fn append_trampoline(ctx: *mut c_void, entry: *const u8, len: usize, index: u64) {
let hook = unsafe { &mut *(ctx as *mut AppendHook) };
let bytes = if entry.is_null() || len == 0 {
&[][..]
} else {
unsafe { core::slice::from_raw_parts(entry, len) }
};
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| hook(bytes, index)));
}