ark-api 0.17.0-pre.15

Ark API
Documentation
//! # 🏃‍♂️ Module Run API
//!
//! This that enables modules to launch instances of other modules, or of itself,
//! asynchronously in order to offload heavy computations

use crate::{ffi::module_run_v0 as ffi, Error, ErrorCode};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

#[doc(hidden)]
pub use ffi::API as FFI_API;

pub use crate::ffi::entrypoints::module_run::ModuleRunFn;

/// Reference to a function a module that can be run
///
/// The function must have the signature as defined in [`ModuleRunFn`]
pub struct Function {
    handle: ffi::FunctionHandle,
}

impl Function {
    /// Create a reference to a function in another module
    pub fn new(fn_module: &str, fn_name: &str) -> Result<Self, Error> {
        ffi::create_function(ffi::FunctionSource::NamedModule, fn_module, fn_name)
            .map_err(Error::from)
            .map(|handle| Self { handle })
    }

    /// Create a reference to a local function in the same module
    pub fn with_local_fn(fn_name: &str) -> Result<Self, Error> {
        ffi::create_function(ffi::FunctionSource::OwnModule, "", fn_name)
            .map_err(Error::from)
            .map(|handle| Self { handle })
    }

    pub(crate) fn handle(&self) -> ffi::FunctionHandle {
        self.handle
    }
}

impl Drop for Function {
    fn drop(&mut self) {
        ffi::remove_function(self.handle).unwrap();
    }
}

/// Block and launch a function separate instance and wait for the result of it
///
/// Note: Prefer to use the asynchronous `launch` function when possible
pub fn blocking_launch(function: &Function, input: &[u8]) -> Result<Vec<u8>, Error> {
    let handle = ffi::blocking_launch(function.handle(), input).map_err(Error::from)?;

    let ready = ffi::is_ready(handle).map_err(Error::from)?;
    assert!(ready.is_ready);

    let mut out_buffer = vec![0; ready.size as usize];
    ffi::retrieve(handle, &mut out_buffer)?;
    Ok(out_buffer)
}

/// Launch a function in a different module as a separate instance
///
/// Returns a future that the user has to drive and poll until completion
pub fn launch(function: &Function, input: &[u8]) -> impl Future<Output = Result<Vec<u8>, Error>> {
    struct LaunchFuture(Result<ffi::RequestHandle, ErrorCode>);

    impl Future for LaunchFuture {
        type Output = Result<Vec<u8>, Error>;
        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            match self.0 {
                Ok(handle) => match ffi::is_ready(handle) {
                    Ok(ffi::IsReady {
                        is_ready: false, ..
                    }) => Poll::Pending,
                    Ok(ffi::IsReady {
                        is_ready: true,
                        size,
                        ..
                    }) => {
                        let mut buffer = vec![0; size as usize];
                        match ffi::retrieve(handle, &mut buffer) {
                            Err(err) => Poll::Ready(Err(err.into())),
                            Ok(()) => Poll::Ready(Ok(buffer)),
                        }
                    }
                    Err(error_code) => Poll::Ready(Err(Error::from(error_code))),
                },
                Err(error_code) => Poll::Ready(Err(Error::from(error_code))),
            }
        }
    }

    LaunchFuture(ffi::launch(function.handle(), input))
}