use crate::{
bindings::{
metacall_clear, metacall_execution_path, metacall_load_from_file, metacall_load_from_memory,
},
cstring_enum,
types::MetaCallLoaderError,
};
use std::{
ffi::CString,
fmt,
os::raw::c_void,
path::{Path, PathBuf},
ptr::null_mut,
};
#[derive(Debug, Clone, Copy)]
pub enum Tag {
C,
Cobol,
Crystal,
CSharp,
Dart,
Deno,
Extension,
File,
Java,
Julia,
JavaScript,
JSM,
Kind,
LLVM,
Lua,
Mock,
NodeJS,
Python,
Ruby,
RPC,
Rust,
TypeScript,
Wasm,
}
impl Tag {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext {
"c" => Some(Tag::C),
"cob" | "cbl" | "cpy" => Some(Tag::Cobol),
"cr" => Some(Tag::Crystal),
"cs" | "vb" => Some(Tag::CSharp),
"dart" => Some(Tag::Dart),
"java" => Some(Tag::Java),
"jl" => Some(Tag::Julia),
"js" | "mjs" | "cjs" | "node" => Some(Tag::NodeJS),
"jsm" => Some(Tag::JSM),
"lua" => Some(Tag::Lua),
"mock" => Some(Tag::Mock),
"py" => Some(Tag::Python),
"rb" => Some(Tag::Ruby),
"rs" => Some(Tag::Rust),
"ts" | "jsx" | "tsx" => Some(Tag::TypeScript),
"wat" | "wasm" => Some(Tag::Wasm),
_ => None,
}
}
pub fn from_package_extension(ext: &str) -> Option<Self> {
match ext {
"dll" => Some(Tag::CSharp),
"wasm" => Some(Tag::Wasm),
"rlib" => Some(Tag::Rust),
_ => None,
}
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Tag::C => write!(f, "c"),
Tag::Cobol => write!(f, "cob"),
Tag::Crystal => write!(f, "cr"),
Tag::CSharp => write!(f, "cs"),
Tag::Dart => write!(f, "dart"),
Tag::Deno => write!(f, "deno"),
Tag::Extension => write!(f, "ext"),
Tag::File => write!(f, "file"),
Tag::Java => write!(f, "java"),
Tag::Julia => write!(f, "jl"),
Tag::JavaScript => write!(f, "js"),
Tag::JSM => write!(f, "jsm"),
Tag::Kind => write!(f, "kind"),
Tag::LLVM => write!(f, "llvm"),
Tag::Lua => write!(f, "lua"),
Tag::Mock => write!(f, "mock"),
Tag::NodeJS => write!(f, "node"),
Tag::Python => write!(f, "py"),
Tag::Ruby => write!(f, "rb"),
Tag::RPC => write!(f, "rpc"),
Tag::Rust => write!(f, "rs"),
Tag::TypeScript => write!(f, "ts"),
Tag::Wasm => write!(f, "wasm"),
}
}
}
pub struct Handle(*mut c_void);
unsafe impl Send for Handle {}
unsafe impl Sync for Handle {}
impl Handle {
pub fn new() -> Self {
Self(null_mut())
}
pub fn as_mut_raw_ptr(&mut self) -> *mut c_void {
self.0
}
}
impl Default for Handle {
fn default() -> Self {
Self::new()
}
}
impl Drop for Handle {
fn drop(&mut self) {
let result = unsafe { metacall_clear(self.0) };
if result != 0 {
eprintln!("Error during cleanup, metacall_clear returned: {}", result);
}
}
}
pub fn from_single_file(
tag: Tag,
path: impl AsRef<Path>,
handle: Option<&mut Handle>,
) -> Result<(), MetaCallLoaderError> {
from_file(tag, [path], handle)
}
pub fn from_file(
tag: Tag,
paths: impl IntoIterator<Item = impl AsRef<Path>>,
handle: Option<&mut Handle>,
) -> Result<(), MetaCallLoaderError> {
let c_tag = cstring_enum!(tag, MetaCallLoaderError)?;
let mut c_path: CString;
let mut new_paths: Vec<*const i8> = Vec::new();
for path in paths.into_iter() {
let path_as_pathbuf = PathBuf::from(path.as_ref());
let path_as_str = path_as_pathbuf.to_str().unwrap();
if !path_as_pathbuf.exists() {
return Err(MetaCallLoaderError::FileNotFound(path_as_pathbuf));
}
if !path_as_pathbuf.is_file() {
return Err(MetaCallLoaderError::NotAFileOrPermissionDenied(
path_as_pathbuf,
));
}
c_path = cstring_enum!(path_as_str, MetaCallLoaderError)?;
new_paths.push(c_path.as_ptr());
}
let handle_ref = match handle {
Some(handle_ptr) => &mut handle_ptr.0,
None => null_mut(),
};
if unsafe {
metacall_load_from_file(
c_tag.as_ptr(),
new_paths.as_mut_ptr(),
new_paths.len(),
handle_ref,
)
} != 0
{
return Err(MetaCallLoaderError::FromFileFailure);
}
Ok(())
}
pub fn from_memory(
tag: Tag,
script: impl ToString,
handle: Option<&mut Handle>,
) -> Result<(), MetaCallLoaderError> {
let script = script.to_string();
let c_tag = cstring_enum!(tag, MetaCallLoaderError)?;
let c_script = cstring_enum!(script, MetaCallLoaderError)?;
let handle_ref = match handle {
Some(handle_ptr) => &mut handle_ptr.0,
None => null_mut(),
};
if unsafe {
metacall_load_from_memory(
c_tag.as_ptr(),
c_script.as_ptr(),
script.len() + 1,
handle_ref,
)
} != 0
{
return Err(MetaCallLoaderError::FromMemoryFailure);
}
Ok(())
}
pub fn execution_path(tag: Tag, path: impl AsRef<Path>) -> Result<(), MetaCallLoaderError> {
let c_tag = cstring_enum!(tag, MetaCallLoaderError)?;
let c_path = cstring_enum!(path.as_ref().to_str().unwrap(), MetaCallLoaderError)?;
if unsafe { metacall_execution_path(c_tag.as_ptr(), c_path.as_ptr()) } != 0 {
return Err(MetaCallLoaderError::ExecutionPathFailure);
}
Ok(())
}