use std::collections::HashMap;
use arora_types::call::{Call, CallError, CallResult};
use derive_more::{Display, Error};
use uuid::Uuid;
use crate::call::{decode_arg, encode_call_result};
#[derive(Display, Debug, Error)]
pub enum DispatchError {
ModuleNotFound {
id: Uuid,
},
FunctionNotFound {
id: Uuid,
},
Trap {
message: String,
},
Internal {
message: String,
},
Guest {
message: String,
},
}
pub trait Module {
fn dispatch(&mut self, function_id: &Uuid, arg: &[u8]) -> Result<Box<[u8]>, DispatchError>;
}
pub type ModuleFn = Box<dyn FnMut(Call) -> Result<CallResult, CallError>>;
pub struct FunctionModule {
id: Uuid,
functions: HashMap<Uuid, ModuleFn>,
}
impl FunctionModule {
pub fn id(&self) -> Uuid {
self.id
}
}
impl Module for FunctionModule {
fn dispatch(&mut self, function_id: &Uuid, arg: &[u8]) -> Result<Box<[u8]>, DispatchError> {
let function = self
.functions
.get_mut(function_id)
.ok_or(DispatchError::FunctionNotFound { id: *function_id })?;
let call =
decode_arg(*function_id, arg).map_err(|message| DispatchError::Internal { message })?;
let result = function(call).map_err(|e| match e {
CallError::Guest { message } => DispatchError::Guest { message },
other => DispatchError::Guest {
message: other.to_string(),
},
})?;
Ok(encode_call_result(*function_id, result))
}
}
pub struct ModuleBuilder {
id: Uuid,
functions: HashMap<Uuid, ModuleFn>,
}
impl ModuleBuilder {
pub fn new(id: Uuid) -> Self {
Self {
id,
functions: HashMap::new(),
}
}
pub fn function(
mut self,
function_id: Uuid,
function: impl FnMut(Call) -> Result<CallResult, CallError> + 'static,
) -> Self {
self.functions.insert(function_id, Box::new(function));
self
}
pub fn build(self) -> FunctionModule {
FunctionModule {
id: self.id,
functions: self.functions,
}
}
}