use std::{
cell::RefCell,
ffi::{CString, c_char, c_void},
sync::{Arc, OnceLock},
};
use anyhow::Result;
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use crate::{
own_string, own_var, shared::var::{pxs_Var, pxs_VarT}
};
pub mod ffi;
pub mod func;
pub mod module;
pub mod object;
pub mod utils;
pub mod var;
#[allow(non_camel_case_types)]
pub type pxs_LoadFileFn = unsafe extern "C" fn(file_path: *const c_char) -> *mut c_char;
#[allow(non_camel_case_types)]
pub type pxs_WriteFileFn = unsafe extern "C" fn(file_path: *const c_char, contents: *const c_char);
#[allow(non_camel_case_types)]
pub type pxs_ReadDirFn = unsafe extern "C" fn(dir_path: *const c_char) -> pxs_VarT;
#[allow(non_camel_case_types)]
pub type pxs_Opaque = *mut c_void;
pub(crate) struct PixelState {
pub load_file: RefCell<Option<pxs_LoadFileFn>>,
pub write_file: RefCell<Option<pxs_WriteFileFn>>,
pub read_dir: RefCell<Option<pxs_ReadDirFn>>,
}
static PIXEL_STATE: OnceLock<ReentrantMutex<PixelState>> = OnceLock::new();
pub(crate) fn get_pixel_state() -> ReentrantMutexGuard<'static, PixelState> {
let mutex = PIXEL_STATE.get_or_init(|| {
ReentrantMutex::new(PixelState {
load_file: RefCell::new(None),
write_file: RefCell::new(None),
read_dir: RefCell::new(None),
})
});
mutex.lock()
}
pub fn read_file(file_path: &str) -> String {
let state = get_pixel_state();
let cbk = state.load_file.borrow();
if cbk.is_none() {
return String::new();
}
let cbk = cbk.unwrap();
let c_str = CString::new(file_path).unwrap();
let file_path_cstr = c_str.as_ptr();
let res = unsafe { cbk(file_path_cstr) };
let res_owned = own_string!(res);
res_owned
}
pub fn write_file(file_path: &str, contents: &str) {
let state = get_pixel_state();
let cbk = state.write_file.borrow();
if cbk.is_none() {
return;
}
let cbk = cbk.unwrap();
let c_file_path = CString::new(file_path).unwrap();
let c_contents = CString::new(contents).unwrap();
unsafe { cbk(c_file_path.as_ptr(), c_contents.as_ptr()) };
}
pub fn read_file_dir(dir_path: &str) -> Vec<String> {
let state = get_pixel_state();
let cbk = state.read_dir.borrow();
if cbk.is_none() {
return vec![];
}
let cbk = cbk.unwrap();
let c_str = CString::new(dir_path).unwrap();
let dir_path_cstr = c_str.as_ptr();
let res = unsafe { cbk(dir_path_cstr) };
if res.is_null() {
return vec![];
}
let var = own_var!(res);
if !var.is_list() {
return vec![];
}
var.get_list()
.unwrap()
.vars
.iter()
.map(|v| v.get_string().unwrap_or(String::new()).clone())
.collect()
}
pub trait PtrMagic: Sized {
fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
fn from_raw(ptr: *mut Self) -> Self {
assert!(!ptr.is_null(), "Attempted to own a null pointer.");
unsafe { *Box::from_raw(ptr) }
}
unsafe fn from_borrow<'a>(ptr: *mut Self) -> &'a mut Self {
unsafe {
assert!(!ptr.is_null(), "Attempted to borrow a null pointer.");
&mut *ptr
}
}
unsafe fn from_borrow_void<'a>(ptr: *mut c_void) -> &'a mut Self {
unsafe { Self::from_borrow(ptr as *mut Self) }
}
}
pub trait PixelScript {
fn start();
fn stop();
fn add_module(source: Arc<module::pxs_Module>);
fn execute(code: &str, file_name: &str) -> Result<pxs_Var>;
fn eval(code: &str) -> Result<pxs_Var>;
fn start_thread();
fn stop_thread();
fn clear_state(call_gc: bool);
fn compile(code: &str, global_scope: pxs_Var) -> Result<pxs_Var>;
fn exec_object(code: pxs_Var, local_scope: pxs_Var) -> Result<pxs_Var>;
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone)]
pub enum pxs_Runtime {
pxs_Lua = 0,
pxs_Python = 1,
pxs_JavaScript = 2,
}
impl pxs_Runtime {
pub fn into_i64(&self) -> i64 {
match self {
pxs_Runtime::pxs_Lua => 0,
pxs_Runtime::pxs_Python => 1,
pxs_Runtime::pxs_JavaScript => 2,
}
}
pub fn from_i64(val: i64) -> Option<Self> {
match val {
0 => Some(Self::pxs_Lua),
1 => Some(Self::pxs_Python),
2 => Some(Self::pxs_JavaScript),
_ => None,
}
}
pub unsafe fn from_var_ptr(var: *mut pxs_Var) -> Option<Self> {
if var.is_null() {
return None;
}
let borrow = unsafe { pxs_Var::from_borrow(var) };
let int_val = borrow.get_i64();
if let Ok(int_val) = int_val {
Self::from_i64(int_val)
} else {
None
}
}
pub fn from_var(var: &pxs_Var) -> Option<Self> {
let int_val = var.get_i64();
if let Ok(int_val) = int_val {
Self::from_i64(int_val)
} else {
None
}
}
pub unsafe fn into_var(&self) -> pxs_Var {
let idx = self.into_i64();
pxs_Var::new_i64(idx)
}
}