use core::ffi::c_int;
use myrmic_common::types::error::EINVAL;
use signal_layer_types::WireType;
use crate::error::{ApiError, ApiResult};
mod c_functions {
use core::ffi::c_int;
#[link(wasm_import_module = "tap")]
unsafe extern "C" {
pub(super) fn tap_resolve(name_ptr: *const u8, name_len: c_int) -> c_int;
pub(super) fn tap_read_retained(
handle: c_int,
buf: *mut u8,
buf_len: c_int,
ts_out_buf: *mut u8,
ts_out_len: c_int,
) -> c_int;
pub(super) fn tap_take_event(handle: c_int, buf: *mut u8, buf_len: c_int) -> c_int;
pub(super) fn tap_list_len() -> c_int;
pub(super) fn tap_list_entry(
index: c_int,
name_ptr: *mut u8,
name_len: c_int,
out_kind_buf: *mut u8,
out_kind_len: c_int,
) -> c_int;
pub(super) fn tap_type_id(handle: c_int, out_id: *mut u8, out_id_len: c_int) -> c_int;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TapKind {
Retained,
Event,
Batch,
Unknown(i32),
}
impl From<c_int> for TapKind {
fn from(v: c_int) -> Self {
match v {
0 => TapKind::Retained,
1 => TapKind::Event,
2 => TapKind::Batch,
n => TapKind::Unknown(n),
}
}
}
#[derive(Debug)]
pub struct Tap {
handle: u32,
type_id: u32,
}
impl Tap {
pub fn resolve(name: &str) -> ApiResult<Option<Tap>> {
let n = unsafe { c_functions::tap_resolve(name.as_ptr(), name.len() as c_int) };
let handle = match n {
n if n >= 0 => n as u32,
-1 => return Ok(None),
n => return Err(ApiError::from(n)),
};
let mut id_buf = [0u8; core::mem::size_of::<u32>()];
let status = unsafe {
c_functions::tap_type_id(handle as c_int, id_buf.as_mut_ptr(), id_buf.len() as c_int)
};
if status != 0 {
return Err(ApiError::from(status));
}
Ok(Some(Self {
handle,
type_id: u32::from_le_bytes(id_buf),
}))
}
#[must_use]
pub fn wire_type_id(&self) -> u32 {
self.type_id
}
pub fn read_retained(&self, buf: &mut [u8]) -> ApiResult<Option<(usize, u64)>> {
let mut ts_buf = [0u8; size_of::<u64>()];
let n = unsafe {
c_functions::tap_read_retained(
self.handle as c_int,
buf.as_mut_ptr(),
buf.len() as c_int,
ts_buf.as_mut_ptr(),
ts_buf.len() as c_int,
)
};
match n {
0 => Ok(None),
n if n > 0 => Ok(Some((n as usize, u64::from_le_bytes(ts_buf)))),
n if n == EINVAL => Err(ApiError::BufferTooSmall),
n => Err(ApiError::from(n)),
}
}
pub fn take_event(&self, buf: &mut [u8]) -> ApiResult<Option<usize>> {
let n = unsafe {
c_functions::tap_take_event(self.handle as c_int, buf.as_mut_ptr(), buf.len() as c_int)
};
match n {
0 => Ok(None),
n if n > 0 => Ok(Some(n as usize)),
n if n == EINVAL => Err(ApiError::BufferTooSmall),
n => Err(ApiError::from(n)),
}
}
pub fn read_typed<T: serde::de::DeserializeOwned + WireType>(
&self,
) -> ApiResult<Option<(u64, T)>> {
self.check_type::<T>()?;
let mut buf = [0u8; 64];
match self.read_retained(&mut buf)? {
None => Ok(None),
Some((n, ts)) => {
let value = decode_strict::<T>(&buf[..n])?;
Ok(Some((ts, value)))
}
}
}
fn check_type<T: WireType>(&self) -> ApiResult<()> {
if T::TYPE_ID != self.type_id {
return Err(ApiError::TypeMismatch {
expected: T::TYPE_ID,
actual: self.type_id,
});
}
Ok(())
}
pub fn take_event_typed<T: serde::de::DeserializeOwned + WireType>(
&self,
) -> ApiResult<Option<T>> {
self.check_type::<T>()?;
let mut buf = [0u8; 64];
match self.take_event(&mut buf)? {
None => Ok(None),
Some(n) => Ok(Some(decode_strict::<T>(&buf[..n])?)),
}
}
}
fn decode_strict<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> ApiResult<T> {
let (value, rest) = postcard::take_from_bytes::<T>(bytes)
.map_err(|_| ApiError::Serde("postcard decode failed"))?;
if !rest.is_empty() {
return Err(ApiError::Serde("trailing bytes after decoded value"));
}
Ok(value)
}
pub fn list_len() -> ApiResult<u32> {
let n = unsafe { c_functions::tap_list_len() };
if n >= 0 {
Ok(n as u32)
} else {
Err(ApiError::from(n))
}
}
pub fn list_entry(index: u32, name_buf: &mut [u8]) -> ApiResult<Option<(usize, TapKind)>> {
let mut kind_buf = [0u8; size_of::<c_int>()];
let n = unsafe {
c_functions::tap_list_entry(
index as c_int,
name_buf.as_mut_ptr(),
name_buf.len() as c_int,
kind_buf.as_mut_ptr(),
kind_buf.len() as c_int,
)
};
match n {
n if n >= 0 => Ok(Some((
n as usize,
TapKind::from(c_int::from_le_bytes(kind_buf)),
))),
-1 => Ok(None),
n => Err(ApiError::from(n)),
}
}