use core::ffi::c_int;
use myrmic_common::types::error::EINVAL;
use super::tap::TapKind;
use signal_layer_types::WireType;
use crate::error::{ApiError, ApiResult};
mod c_functions {
use core::ffi::c_int;
#[link(wasm_import_module = "outlet")]
unsafe extern "C" {
pub(super) fn outlet_resolve(name_ptr: *const u8, name_len: c_int) -> c_int;
pub(super) fn outlet_write_retained(handle: c_int, buf: *const u8, buf_len: c_int)
-> c_int;
pub(super) fn outlet_list_len() -> c_int;
pub(super) fn outlet_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 outlet_type_id(handle: c_int, out_id: *mut u8, out_id_len: c_int) -> c_int;
}
}
#[derive(Debug)]
pub struct Outlet {
handle: u32,
type_id: u32,
}
impl Outlet {
pub fn resolve(name: &str) -> ApiResult<Option<Outlet>> {
let n = unsafe { c_functions::outlet_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::outlet_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 write(&self, bytes: &[u8]) -> ApiResult<()> {
let status = unsafe {
c_functions::outlet_write_retained(
self.handle as c_int,
bytes.as_ptr(),
bytes.len() as c_int,
)
};
match status {
0 => Ok(()),
n if n == EINVAL => Err(ApiError::Serde("outlet rejected command payload")),
n => Err(ApiError::from(n)),
}
}
pub fn write_typed<T: serde::Serialize + WireType>(&self, value: &T) -> ApiResult<()> {
if T::TYPE_ID != self.type_id {
return Err(ApiError::TypeMismatch {
expected: T::TYPE_ID,
actual: self.type_id,
});
}
let mut buf = [0u8; 64];
let used = postcard::to_slice(value, &mut buf).map_err(|_| ApiError::BufferTooSmall)?;
self.write(used)
}
}
pub fn list_len() -> ApiResult<u32> {
let n = unsafe { c_functions::outlet_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::outlet_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)),
}
}