use super::descriptor::AsDescriptor;
use super::error::{Error, Result};
use super::helpers::{call_buffer_len, call_buffer_str, call_string};
use super::Address;
use crate::panic_or_trap;
use core::convert::TryFrom;
use core::fmt::{Display, Formatter};
use core::marker::PhantomData;
use core::num::NonZeroUsize;
use core::ptr;
use oc_wasm_sys::component as sys;
pub struct Lister(());
impl Lister {
#[must_use = "A Lister can only be taken once. It needs to be saved. Discarding it means it is impossible to ever list components."]
pub fn take() -> Option<Self> {
static mut INSTANCE: Option<Lister> = Some(Self(()));
unsafe { INSTANCE.take() }
}
#[allow(clippy::unused_self)] pub fn start<'lister>(&'lister mut self, component_type: Option<&str>) -> Listing<'lister> {
let result =
unsafe{call_string(sys::list_start, component_type)};
result.unwrap_or_else(|_| panic_or_trap!("unreachable"));
Listing(PhantomData)
}
}
#[must_use = "Starting a component listing is only useful if you read the results."]
pub struct Listing<'lister>(PhantomData<&'lister mut Lister>);
impl<'lister> Listing<'lister> {
#[allow(clippy::should_implement_trait)] #[allow(clippy::unused_self)] pub fn next<'listing>(&'listing mut self) -> Option<ListEntry<'listing, 'lister>> {
let mut buf = uuid::Bytes::default();
let rc = unsafe { sys::list_next(buf.as_mut_ptr()) };
if rc == 0 {
None
} else {
let address = Address::from_bytes(buf);
Some(ListEntry {
address,
listing: PhantomData,
})
}
}
}
#[derive(Debug)]
pub struct ListEntry<'listing, 'lister> {
address: Address,
listing: PhantomData<&'listing mut Listing<'lister>>,
}
impl ListEntry<'_, '_> {
#[must_use = "This function is only useful for its return value"]
pub fn address(&self) -> &Address {
&self.address
}
#[allow(clippy::unused_self)] #[must_use = "This function is only useful for its return value"]
pub fn type_name_len(&self) -> NonZeroUsize {
let len = unsafe { call_buffer_len(sys::list_type) }
.unwrap_or_else(|_| panic_or_trap!("unreachable"));
unsafe { NonZeroUsize::new_unchecked(len) }
}
#[allow(clippy::unused_self)] #[must_use = "This function is only useful for its return value"]
pub fn type_name<'buffer>(&self, buffer: &'buffer mut [u8]) -> Result<&'buffer mut str> {
unsafe { call_buffer_str(sys::list_type, buffer) }
}
}
#[allow(clippy::module_name_repetitions)] #[must_use = "This function is only useful for its return value"]
pub fn component_type_len(address: &Address) -> Result<NonZeroUsize> {
let address = address.as_bytes();
let len = Error::from_isize(
unsafe { sys::component_type(address.as_ptr(), ptr::null_mut(), 0) },
)?;
Ok(unsafe { NonZeroUsize::new_unchecked(len) })
}
#[allow(clippy::module_name_repetitions)] #[must_use = "This function is only useful for its return value"]
pub fn component_type<'buf>(address: &Address, buffer: &'buf mut [u8]) -> Result<&'buf mut str> {
let address = address.as_bytes();
let bytes_written = {
let buf_len = buffer.len();
let buf_ptr = buffer.as_mut_ptr();
Error::from_isize(
unsafe { sys::component_type(address.as_ptr(), buf_ptr, buf_len) },
)?
};
Ok(
unsafe { core::str::from_utf8_unchecked_mut(buffer.get_unchecked_mut(0..bytes_written)) },
)
}
#[must_use = "This function is only useful for its return value"]
pub fn slot(address: &Address) -> Result<u32> {
let address = address.as_bytes();
Error::from_i32(
unsafe { sys::slot(address.as_ptr(), address.len()) },
)
}
pub struct MethodLister(());
impl MethodLister {
#[must_use = "A Lister can only be taken once. It needs to be saved. Discarding it means it is impossible to ever list methods."]
pub fn take() -> Option<Self> {
static mut INSTANCE: Option<MethodLister> = Some(Self(()));
unsafe { INSTANCE.take() }
}
#[allow(clippy::module_name_repetitions)] #[allow(clippy::unused_self)] pub fn start_component<'lister>(
&'lister mut self,
address: &Address,
) -> Result<MethodListing<'lister>> {
let address = address.as_bytes();
Error::from_i32(unsafe { sys::methods_start_component(address.as_ptr()) })?;
Ok(MethodListing(PhantomData))
}
#[allow(clippy::unused_self)] pub fn start_value(&mut self, descriptor: &impl AsDescriptor) -> MethodListing<'_> {
unsafe { sys::methods_start_value(descriptor.as_descriptor().as_raw()) };
MethodListing(PhantomData)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct MethodAttributes {
pub direct: bool,
pub getter: bool,
pub setter: bool,
}
impl From<u32> for MethodAttributes {
fn from(value: u32) -> Self {
Self {
direct: (value & 1) != 0,
getter: (value & 2) != 0,
setter: (value & 4) != 0,
}
}
}
#[must_use = "Starting a method listing is only useful if you read the results."]
pub struct MethodListing<'lister>(PhantomData<&'lister mut MethodLister>);
impl MethodListing<'_> {
#[allow(clippy::unused_self)] #[must_use = "This function is only useful for its return value"]
pub fn next_len(&self) -> Option<NonZeroUsize> {
let result = Error::from_isize(
unsafe { sys::methods_next(ptr::null_mut(), 0, ptr::null_mut()) },
);
NonZeroUsize::new(result.unwrap_or_else(|_| panic_or_trap!("unreachable")))
}
#[allow(clippy::unused_self)] pub fn next<'buffer>(
&mut self,
buffer: &'buffer mut [u8],
) -> Result<Option<(&'buffer mut str, MethodAttributes)>> {
let mut attributes = 0_u32;
let bytes_written = {
let len = buffer.len();
let ptr = buffer.as_mut_ptr();
Error::from_isize(
unsafe { sys::methods_next(ptr, len, &mut attributes) },
)?
};
if bytes_written == 0 {
Ok(None)
} else {
Ok(Some((
unsafe {
core::str::from_utf8_unchecked_mut(buffer.get_unchecked_mut(0..bytes_written))
},
attributes.into(),
)))
}
}
}
#[must_use = "This function is only useful for its return value"]
pub fn documentation_component_length(address: &Address, method: &str) -> Result<usize> {
let address = address.as_bytes();
Error::from_isize(
unsafe {
sys::documentation_component(
address.as_ptr(),
method.as_ptr(),
method.len(),
ptr::null_mut(),
0,
)
},
)
}
#[allow(clippy::module_name_repetitions)] #[must_use = "This function is only useful for its return value"]
pub fn documentation_component<'buf>(
address: &Address,
method: &str,
buffer: &'buf mut [u8],
) -> Result<&'buf mut str> {
let address = address.as_bytes();
let bytes_written = {
let buf_len = buffer.len();
let buf_ptr = buffer.as_mut_ptr();
Error::from_isize(
unsafe {
sys::documentation_component(
address.as_ptr(),
method.as_ptr(),
method.len(),
buf_ptr,
buf_len,
)
},
)?
};
Ok(
unsafe { core::str::from_utf8_unchecked_mut(buffer.get_unchecked_mut(0..bytes_written)) },
)
}
#[must_use = "This function is only useful for its return value"]
pub fn documentation_value_length(descriptor: &impl AsDescriptor, method: &str) -> Result<usize> {
Error::from_isize(
unsafe {
sys::documentation_value(
descriptor.as_descriptor().as_raw(),
method.as_ptr(),
method.len(),
ptr::null_mut(),
0,
)
},
)
}
#[must_use = "This function is only useful for its return value"]
pub fn documentation_value<'buf>(
descriptor: &impl AsDescriptor,
method: &str,
buffer: &'buf mut [u8],
) -> Result<&'buf mut str> {
let bytes_written = {
let buf_len = buffer.len();
let buf_ptr = buffer.as_mut_ptr();
Error::from_isize(
unsafe {
sys::documentation_value(
descriptor.as_descriptor().as_raw(),
method.as_ptr(),
method.len(),
buf_ptr,
buf_len,
)
},
)?
};
Ok(
unsafe { core::str::from_utf8_unchecked_mut(buffer.get_unchecked_mut(0..bytes_written)) },
)
}
pub struct Invoker(());
impl Invoker {
#[must_use = "An Invoker can only be taken once. It needs to be saved. Discarding it means it is impossible to ever make a method call."]
pub fn take() -> Option<Self> {
static mut INSTANCE: Option<Invoker> = Some(Self(()));
unsafe { INSTANCE.take() }
}
#[allow(clippy::unused_self)] pub fn component_method<'invoker>(
&'invoker mut self,
address: &Address,
method: &str,
params: Option<&[u8]>,
) -> Result<(InvokeResult, MethodCall<'invoker>)> {
let address = address.as_bytes();
let params_ptr = params.map_or(ptr::null(), <[u8]>::as_ptr);
let done = Error::from_i32(
unsafe {
sys::invoke_component_method(
address.as_ptr(),
method.as_ptr(),
method.len(),
params_ptr,
)
},
)? != 0;
Ok((
if done {
InvokeResult::Complete
} else {
InvokeResult::Incomplete
},
MethodCall(PhantomData),
))
}
#[allow(clippy::unused_self)] pub fn value<'invoker>(
&'invoker mut self,
descriptor: &impl AsDescriptor,
params: Option<&[u8]>,
) -> Result<(InvokeResult, MethodCall<'invoker>)> {
let params_ptr = params.map_or(ptr::null(), <[u8]>::as_ptr);
let done = Error::from_i32(
unsafe { sys::invoke_value(descriptor.as_descriptor().as_raw(), params_ptr) },
)? != 0;
Ok((
if done {
InvokeResult::Complete
} else {
InvokeResult::Incomplete
},
MethodCall(PhantomData),
))
}
#[allow(clippy::unused_self)] pub fn value_indexed_read<'invoker>(
&'invoker mut self,
descriptor: &impl AsDescriptor,
params: Option<&[u8]>,
) -> Result<(InvokeResult, MethodCall<'invoker>)> {
let params_ptr = params.map_or(ptr::null(), <[u8]>::as_ptr);
let done = Error::from_i32(
unsafe {
sys::invoke_value_indexed_read(descriptor.as_descriptor().as_raw(), params_ptr)
},
)? != 0;
Ok((
if done {
InvokeResult::Complete
} else {
InvokeResult::Incomplete
},
MethodCall(PhantomData),
))
}
#[allow(clippy::unused_self)] pub fn value_indexed_write<'invoker>(
&'invoker mut self,
descriptor: &impl AsDescriptor,
params: Option<&[u8]>,
) -> Result<(InvokeResult, MethodCall<'invoker>)> {
let params_ptr = params.map_or(ptr::null(), <[u8]>::as_ptr);
let done = Error::from_i32(
unsafe {
sys::invoke_value_indexed_write(descriptor.as_descriptor().as_raw(), params_ptr)
},
)? != 0;
Ok((
if done {
InvokeResult::Complete
} else {
InvokeResult::Incomplete
},
MethodCall(PhantomData),
))
}
#[allow(clippy::unused_self)] pub fn value_method<'invoker>(
&'invoker mut self,
descriptor: &impl AsDescriptor,
method: &str,
params: Option<&[u8]>,
) -> Result<(InvokeResult, MethodCall<'invoker>)> {
let params_ptr = params.map_or(ptr::null(), <[u8]>::as_ptr);
let done = Error::from_i32(
unsafe {
sys::invoke_value_method(
descriptor.as_descriptor().as_raw(),
method.as_ptr(),
method.len(),
params_ptr,
)
},
)? != 0;
Ok((
if done {
InvokeResult::Complete
} else {
InvokeResult::Incomplete
},
MethodCall(PhantomData),
))
}
}
impl core::fmt::Debug for Invoker {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Invoker")
}
}
impl PartialEq for Invoker {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl Eq for Invoker {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum InvokeResult {
Complete,
Incomplete,
}
#[derive(Debug, Eq, PartialEq)]
#[must_use = "Discarding a MethodCall immediately is buggy. Even if you know the method you are calling is direct and don’t need its return value, direct methods must be run indirectly if the method call cost limit is reached, so you still need to make sure it finishes."]
pub struct MethodCall<'invoker>(PhantomData<&'invoker mut Invoker>);
impl<'invoker> MethodCall<'invoker> {
#[must_use = "This function is only useful for its return value"]
pub fn end_length(self) -> InvokeEndLengthResult<'invoker> {
let ret = unsafe { sys::invoke_end(ptr::null_mut(), 0) };
match MethodCallError::from_isize(PhantomData, ret) {
Ok(n) => InvokeEndLengthResult::Done(Ok((n, self))),
Err(MethodCallError::QueueEmpty) => InvokeEndLengthResult::Pending(self),
Err(e) => InvokeEndLengthResult::Done(Err(e)),
}
}
pub unsafe fn end_ptr(self, buffer: *mut u8, len: usize) -> InvokeEndResult<'invoker> {
let result = sys::invoke_end(buffer, len);
match MethodCallError::from_isize(PhantomData, result) {
Err(MethodCallError::BufferTooShort) => InvokeEndResult::BufferTooShort(self),
Err(MethodCallError::QueueEmpty) => InvokeEndResult::Pending(self),
other => InvokeEndResult::Done(other),
}
}
pub fn end(self, buffer: &mut [u8]) -> InvokeEndResult<'invoker> {
let result = unsafe { sys::invoke_end(buffer.as_mut_ptr(), buffer.len()) };
match MethodCallError::from_isize(PhantomData, result) {
Err(MethodCallError::BufferTooShort) => InvokeEndResult::BufferTooShort(self),
Err(MethodCallError::QueueEmpty) => InvokeEndResult::Pending(self),
other => InvokeEndResult::Done(other),
}
}
}
impl Drop for MethodCall<'_> {
fn drop(&mut self) {
unsafe { sys::invoke_cancel() }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct LastException<'invoker>(PhantomData<&'invoker mut Invoker>);
impl<'invoker> LastException<'invoker> {
fn new(_: PhantomData<&'invoker mut Invoker>) -> Self {
Self(PhantomData)
}
#[allow(clippy::unused_self)] #[must_use = "This function is only useful for its return value"]
pub fn message_length(&self) -> usize {
let result = unsafe { call_buffer_len(sys::last_exception_message) };
result.unwrap_or_else(|_| panic_or_trap!("unreachable"))
}
#[allow(clippy::unused_self)] pub fn message<'buf>(&self, buffer: &'buf mut [u8]) -> Result<&'buf str> {
let result = unsafe { call_buffer_str(sys::last_exception_message, buffer) };
match result {
Ok(message) => Ok(message),
Err(Error::BufferTooShort) => Err(Error::BufferTooShort),
Err(_) => panic_or_trap!("unreachable"),
}
}
#[allow(clippy::unused_self)] #[must_use = "This function is only useful for its return value"]
pub fn is_type(&self, class: &str) -> bool {
let result = unsafe { call_string(sys::last_exception_is_type, Some(class)) };
result.unwrap_or_else(|_| panic_or_trap!("unreachable")) != 0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MethodCallError<'invoker> {
CborDecode,
BufferTooShort,
NoSuchComponent,
NoSuchMethod,
BadParameters(LastException<'invoker>),
QueueFull,
QueueEmpty,
BadDescriptor,
TooManyDescriptors,
Other(LastException<'invoker>),
Unknown,
}
impl<'invoker> MethodCallError<'invoker> {
fn from_isize(
_: PhantomData<&'invoker mut Invoker>,
value: isize,
) -> core::result::Result<usize, MethodCallError<'invoker>> {
match value {
-1 => panic_or_trap!("Memory fault"), -2 => Err(Self::CborDecode),
-3 => panic_or_trap!("String decode error"), -4 => Err(Self::BufferTooShort),
-5 => Err(Self::NoSuchComponent),
-6 => Err(Self::NoSuchMethod),
-7 => Err(Self::BadParameters(LastException::new(PhantomData))),
-8 => Err(Self::QueueFull),
-9 => Err(Self::QueueEmpty),
-10 => Err(Self::BadDescriptor),
-11 => Err(Self::TooManyDescriptors),
-12 => Err(Self::Other(LastException::new(PhantomData))),
x if x < 0 => Err(Self::Unknown),
_ => {
#[allow(clippy::cast_sign_loss)]
Ok(value as usize)
}
}
}
#[must_use = "This function is only useful for its return value"]
pub fn as_str(self) -> &'static str {
self.simplify().as_str()
}
#[must_use = "This function is only useful for its return value"]
pub fn simplify(self) -> Error {
match self {
Self::CborDecode => Error::CborDecode,
Self::BufferTooShort => Error::BufferTooShort,
Self::NoSuchComponent => Error::NoSuchComponent,
Self::NoSuchMethod => Error::NoSuchMethod,
Self::BadParameters(_) => Error::BadParameters,
Self::QueueFull => Error::QueueFull,
Self::QueueEmpty => Error::QueueEmpty,
Self::BadDescriptor => Error::BadDescriptor,
Self::TooManyDescriptors => Error::TooManyDescriptors,
Self::Other(_) => Error::Other,
Self::Unknown => Error::Unknown,
}
}
}
impl Display for MethodCallError<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.simplify().fmt(f)
}
}
impl From<MethodCallError<'_>> for Error {
fn from(source: MethodCallError<'_>) -> Self {
source.simplify()
}
}
impl TryFrom<Error> for MethodCallError<'static> {
type Error = ();
fn try_from(source: Error) -> core::result::Result<Self, Self::Error> {
match source {
Error::CborDecode => Ok(Self::CborDecode),
Error::BufferTooShort => Ok(Self::BufferTooShort),
Error::NoSuchComponent => Ok(Self::NoSuchComponent),
Error::NoSuchMethod => Ok(Self::NoSuchMethod),
Error::QueueFull => Ok(Self::QueueFull),
Error::QueueEmpty => Ok(Self::QueueEmpty),
Error::BadDescriptor => Ok(Self::BadDescriptor),
Error::TooManyDescriptors => Ok(Self::TooManyDescriptors),
Error::Unknown => Ok(Self::Unknown),
Error::BadParameters | Error::Other => Err(()),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for MethodCallError<'_> {}
#[derive(Debug, Eq, PartialEq)]
pub enum InvokeEndLengthResult<'invoker> {
Done(core::result::Result<(usize, MethodCall<'invoker>), MethodCallError<'invoker>>),
Pending(MethodCall<'invoker>),
}
#[derive(Debug, Eq, PartialEq)]
pub enum InvokeEndResult<'invoker> {
Done(core::result::Result<usize, MethodCallError<'invoker>>),
BufferTooShort(MethodCall<'invoker>),
Pending(MethodCall<'invoker>),
}
impl<'invoker> InvokeEndResult<'invoker> {
#[must_use = "This function is only useful for its return value"]
pub fn expect_done(self) -> core::result::Result<usize, MethodCallError<'invoker>> {
match self {
Self::Done(result) => result,
Self::BufferTooShort(_) => Err(MethodCallError::BufferTooShort),
Self::Pending(_) => Err(MethodCallError::QueueEmpty),
}
}
}