use myrmic_common::cells::{ClassRef, SpawnError, SpawnRequest, TerminateError};
use myrmic_common::types::error::SUCCESS;
use crate::{Encoder, Sri, String, Vec};
mod c_functions {
use core::ffi::c_int;
#[link(wasm_import_module = "cell")]
unsafe extern "C" {
pub(super) fn spawn_cell_host(buffer: *const u8, length: c_int, out_sri: *mut u8) -> c_int;
pub(super) fn terminate_cell_host(buffer: *const u8, length: c_int) -> c_int;
pub(super) fn stop_self_host(code_present: u32, code: u32) -> c_int;
}
}
pub fn stop_self(code: Option<u32>) {
unsafe {
c_functions::stop_self_host(u32::from(code.is_some()), code.unwrap_or_default());
}
}
pub fn spawn_cell(request: &SpawnRequest) -> Result<Sri, SpawnError> {
let payload = postcard::to_allocvec(request).map_err(|_| SpawnError::DeployFailed)?;
let mut out_sri = [0u8; 16];
let status = unsafe {
c_functions::spawn_cell_host(
payload.as_ptr(),
payload.len() as core::ffi::c_int,
out_sri.as_mut_ptr(),
)
};
match status {
code if code == SUCCESS => Ok(Sri::from_bytes(out_sri)),
code => Err(SpawnError::try_from(code)
.unwrap_or_else(|code| panic!("unexpected spawn error code from host: {code}"))),
}
}
#[derive(Clone, Copy)]
pub struct ClassHandle {
hash: &'static [u8; 32],
}
impl ClassHandle {
#[doc(hidden)]
pub const fn from_hash_ref(hash: &'static [u8; 32]) -> Self {
Self { hash }
}
pub fn class_ref(&self) -> ClassRef {
ClassRef::Hash(unsafe { core::ptr::read_volatile(self.hash as *const [u8; 32]) })
}
#[allow(clippy::new_ret_no_self)]
pub fn new(&self) -> SpawnBuilder {
SpawnBuilder {
handle: *self,
local_name: None,
tags: None,
arguments: None,
detached: false,
grace_ms: None,
deadline_ms: None,
}
}
pub fn spawn(&self) -> Result<Sri, SpawnError> {
self.new().spawn()
}
}
#[derive(Clone)]
pub struct SpawnBuilder {
handle: ClassHandle,
local_name: Option<String>,
tags: Option<Vec<String>>,
arguments: Option<Result<Vec<u8>, ()>>,
detached: bool,
grace_ms: Option<u64>,
deadline_ms: Option<u64>,
}
impl SpawnBuilder {
pub fn name(mut self, name: impl Into<String>) -> Self {
self.local_name = Some(name.into());
self
}
pub fn tag(mut self, tag: impl Into<String>) -> Self {
self.tags.get_or_insert_with(Vec::new).push(tag.into());
self
}
pub fn payload<T: Encoder + ?Sized>(mut self, payload: &T) -> Self {
self.arguments = Some(payload.to_bytes().map_err(|_| ()));
self
}
pub fn detached(mut self) -> Self {
self.detached = true;
self
}
pub fn grace(mut self, grace: core::time::Duration) -> Self {
self.grace_ms = Some(grace.as_millis() as u64);
self
}
pub fn deadline(mut self, deadline: core::time::Duration) -> Self {
self.deadline_ms = Some(deadline.as_millis() as u64);
self
}
pub fn spawn(self) -> Result<Sri, SpawnError> {
let arguments = match self.arguments {
None => None,
Some(Ok(bytes)) => Some(bytes),
Some(Err(())) => return Err(SpawnError::DeployFailed),
};
spawn_cell(&SpawnRequest {
class: self.handle.class_ref(),
local_name: self.local_name,
tags: self.tags,
arguments,
detached: self.detached,
grace_ms: self.grace_ms,
deadline_ms: self.deadline_ms,
})
}
}
pub fn terminate_cell(sri: &str) -> Result<(), TerminateError> {
let bytes = sri.as_bytes();
let status = unsafe {
c_functions::terminate_cell_host(bytes.as_ptr(), bytes.len() as core::ffi::c_int)
};
match status {
code if code == SUCCESS => Ok(()),
code => Err(TerminateError::try_from(code)
.unwrap_or_else(|code| panic!("unexpected terminate error code from host: {code}"))),
}
}