#[macro_use]
extern crate dlopen_derive;
use safer_ffi::prelude::*;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::HashMap};
mod allocation;
pub use allocation::*;
pub use ty::Erased;
mod ty {
use safer_ffi::derive_ReprC;
#[derive_ReprC]
#[ReprC::opaque]
pub struct Erased {
_private: (),
}
}
pub type TypePath = String;
pub type ScriptApi = HashMap<TypePath, ScriptType>;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ScriptType {
Struct(StructDefinition),
Function(FunctionDefinition),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StructDefinition {
pub layout: DataLayout,
pub component_type: DataType,
pub method_definitions: Vec<FunctionDefinition>,
}
impl HasDataLayout for StructDefinition {
fn get_data_layout(&self) -> DataLayout {
self.layout
}
}
pub trait HasDataLayout {
fn get_data_layout(&self) -> DataLayout;
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct DataLayout {
size: usize,
align: usize,
}
impl DataLayout {
pub fn from_size_align(size: usize, align: usize) -> Result<Self, std::alloc::LayoutError> {
std::alloc::Layout::from_size_align(size, align)?;
Ok(Self { size, align })
}
pub fn size(&self) -> usize {
self.size
}
pub fn align(&self) -> usize {
self.align
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum DataType {
Pointer(Box<ScriptType>),
Struct {
fields: HashMap<String, StructDefinition>,
},
Primitive(Primitive),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Primitive {
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Char,
Bool,
}
impl HasDataLayout for Primitive {
#[rustfmt::skip]
fn get_data_layout(&self) -> DataLayout {
match self {
Primitive::Char => DataLayout::from_size_align(1, 1).unwrap(),
Primitive::Bool => DataLayout::from_size_align(1, 1).unwrap(),
Primitive::U8 => DataLayout::from_size_align(1, 1).unwrap(),
Primitive::U16 => DataLayout::from_size_align(2, 2).unwrap(),
Primitive::U32 => DataLayout::from_size_align(4, 4).unwrap(),
Primitive::U64 => DataLayout::from_size_align(8, 8).unwrap(),
Primitive::U128 => DataLayout::from_size_align(16, 16).unwrap(),
Primitive::I8 => DataLayout::from_size_align(1, 1).unwrap(),
Primitive::I16 => DataLayout::from_size_align(2, 2).unwrap(),
Primitive::I32 => DataLayout::from_size_align(4, 4).unwrap(),
Primitive::I64 => DataLayout::from_size_align(8, 8).unwrap(),
Primitive::I128 => DataLayout::from_size_align(16, 16).unwrap(),
Primitive::F32 => DataLayout::from_size_align(4, 4).unwrap(),
Primitive::F64 => DataLayout::from_size_align(8, 8).unwrap(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FunctionDefinition {
pub arguments: HashMap<Cow<'static, str>, TypePath>,
pub return_type: Option<TypePath>,
}
pub use language_adapter::*;
#[allow(missing_docs)]
mod language_adapter {
use super::{Erased, ScriptApi};
use dlopen::wrapper::{Container, WrapperApi};
use safer_ffi::{prelude::*, string::str_ref};
#[derive_ReprC]
#[repr(C)]
pub struct LanguageAdapterInitArgs {
pub log_info: extern "C" fn(safer_ffi::string::String),
}
pub struct LanguageAdapter(Container<LanguageAdapterCApi>);
impl LanguageAdapter {
pub fn new(api: Container<LanguageAdapterCApi>) -> Self {
Self(api)
}
pub fn init_adapter(&self, args: &LanguageAdapterInitArgs) {
self.0.init_adapter(args)
}
pub fn get_api(&self) -> Result<ScriptApi, serde_cbor::Error> {
serde_cbor::from_slice(&self.0.get_api())
}
pub fn run_function(&self, path: &str, args: &[*const Erased]) -> *const Erased {
(self.0.run_function)(path.into(), args.into())
}
}
#[derive(WrapperApi)]
pub struct LanguageAdapterCApi {
init_adapter: fn(args: &LanguageAdapterInitArgs),
get_api: fn() -> safer_ffi::Vec<u8>,
run_function: fn(path: str_ref, args: c_slice::Ref<*const Erased>) -> *const Erased,
}
}