#![allow(clippy::must_use_candidate)]
use num_derive::FromPrimitive;
use std::{
collections::HashSet,
ffi::{CStr, CString},
};
extern crate yaslapi_sys;
mod aux;
use yaslapi_sys::YASL_State;
pub type CFunction = unsafe extern "C" fn(*mut YASL_State) -> i32;
#[allow(clippy::cast_possible_wrap)]
#[derive(Debug, FromPrimitive, PartialEq)]
#[repr(u32)]
pub enum StateResult {
Success = yaslapi_sys::YASL_Error_YASL_SUCCESS,
ModuleSuccess = yaslapi_sys::YASL_Error_YASL_MODULE_SUCCESS,
Error = yaslapi_sys::YASL_Error_YASL_ERROR,
InitError = yaslapi_sys::YASL_Error_YASL_INIT_ERROR,
SyntaxError = yaslapi_sys::YASL_Error_YASL_SYNTAX_ERROR,
TypeError = yaslapi_sys::YASL_Error_YASL_TYPE_ERROR,
DivideByZeroError = yaslapi_sys::YASL_Error_YASL_DIVIDE_BY_ZERO_ERROR,
ValueError = yaslapi_sys::YASL_Error_YASL_VALUE_ERROR,
TooManyVarError = yaslapi_sys::YASL_Error_YASL_TOO_MANY_VAR_ERROR,
PlatformNotSupp = yaslapi_sys::YASL_Error_YASL_PLATFORM_NOT_SUPP,
AssertError = yaslapi_sys::YASL_Error_YASL_ASSERT_ERROR,
StackOverflowError = yaslapi_sys::YASL_Error_YASL_STACK_OVERFLOW_ERROR,
}
#[allow(clippy::cast_possible_wrap)]
#[derive(Debug, FromPrimitive, PartialEq)]
#[repr(i32)]
pub enum Type {
Undef = yaslapi_sys::YASL_Types_Y_UNDEF,
Float = yaslapi_sys::YASL_Types_Y_FLOAT,
Int = yaslapi_sys::YASL_Types_Y_INT,
Bool = yaslapi_sys::YASL_Types_Y_BOOL,
Str = yaslapi_sys::YASL_Types_Y_STR,
List = yaslapi_sys::YASL_Types_Y_LIST,
Table = yaslapi_sys::YASL_Types_Y_TABLE,
Fn = yaslapi_sys::YASL_Types_Y_FN,
Closure = yaslapi_sys::YASL_Types_Y_CLOSURE,
CFn = yaslapi_sys::YASL_Types_Y_CFN,
UserPtr = yaslapi_sys::YASL_Types_Y_USERPTR,
UserData = yaslapi_sys::YASL_Types_Y_USERDATA,
}
pub struct State {
state: *mut YASL_State,
global_ids: HashSet<CString>,
}
impl State {
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn from_path(script_location: &str) -> Self {
let script_location = CString::new(script_location).unwrap();
Self::new(unsafe { yaslapi_sys::YASL_newstate(script_location.as_ptr()) })
}
#[must_use]
pub fn from_source(source: &str) -> Self {
Self::new(unsafe { yaslapi_sys::YASL_newstate_bb(source.as_ptr().cast(), source.len()) })
}
#[must_use]
fn new(state: *mut YASL_State) -> Self {
assert!(!state.is_null());
Self {
state,
global_ids: HashSet::new(),
}
}
pub fn compile(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_compile(self.state) }.into()
}
pub fn declare_global(&mut self, name: &str) -> i32 {
let var_name = CString::new(name).unwrap();
assert!(!self.global_ids.contains(&var_name));
let name_pointer = var_name.as_ptr();
self.global_ids.insert(var_name);
unsafe { yaslapi_sys::YASL_declglobal(self.state, name_pointer) }
}
pub fn declare_lib_collections(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_collections(self.state) }
}
pub fn declare_lib_error(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_error(self.state) }
}
pub fn declare_lib_io(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_io(self.state) }
}
pub fn declare_lib_math(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_math(self.state) }
}
pub fn declare_lib_require(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_require(self.state) }
}
pub fn declare_lib_require_c(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_require_c(self.state) }
}
pub fn declare_lib_mt(&mut self) -> i32 {
unsafe { yaslapi_sys::YASL_decllib_mt(self.state) }
}
pub fn clone_top(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_duptop(self.state) }.into()
}
pub fn execute(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_execute(self.state) }.into()
}
pub fn execute_repl(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_execute_REPL(self.state) }.into()
}
pub fn function_call(&mut self, n: usize) -> StateResult {
unsafe {
yaslapi_sys::YASL_functioncall(
self.state,
n.try_into().expect(
"The input argument count cannout be safely converted to a C signed integer.",
),
)
}
.into()
}
pub fn is_bool(&self) -> bool {
unsafe { yaslapi_sys::YASL_isbool(self.state) }
}
pub fn is_float(&self) -> bool {
unsafe { yaslapi_sys::YASL_isfloat(self.state) }
}
pub fn is_int(&self) -> bool {
unsafe { yaslapi_sys::YASL_isint(self.state) }
}
pub fn is_list(&self) -> bool {
unsafe { yaslapi_sys::YASL_islist(self.state) }
}
pub fn is_str(&self) -> bool {
unsafe { yaslapi_sys::YASL_isstr(self.state) }
}
pub fn is_table(&self) -> bool {
unsafe { yaslapi_sys::YASL_istable(self.state) }
}
pub fn is_undef(&self) -> bool {
unsafe { yaslapi_sys::YASL_isundef(self.state) }
}
pub fn is_userdata(&self, tag: &CString) -> bool {
unsafe { yaslapi_sys::YASL_isuserdata(self.state, tag.as_ptr()) }
}
pub fn is_userptr(&self) -> bool {
unsafe { yaslapi_sys::YASL_isuserptr(self.state) }
}
pub fn is_n_bool(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnbool(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_float(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnfloat(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_int(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnint(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_list(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnlist(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_str(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnstr(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_table(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isntable(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_undef(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnundef(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_userdata(&mut self, tag: &str, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnuserdata(
self.state,
tag.as_ptr().cast(),
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn is_n_userptr(&mut self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_isnuserptr(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn len(&mut self) {
unsafe { yaslapi_sys::YASL_len(self.state) }
}
pub fn list_get(&mut self, n: isize) -> StateResult {
unsafe {
yaslapi_sys::YASL_listget(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a 64-bit signed integer."),
)
}
.into()
}
pub fn list_push(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_listpush(self.state) }.into()
}
pub fn load_global(&mut self, name: &str) -> StateResult {
let name = CString::new(name).unwrap();
unsafe { yaslapi_sys::YASL_loadglobal(self.state, name.as_ptr()) }.into()
}
pub fn load_mt(&mut self, name: &str) -> StateResult {
let name = CString::new(name).unwrap();
unsafe { yaslapi_sys::YASL_loadmt(self.state, name.as_ptr()) }.into()
}
pub fn peek_bool(&self) -> bool {
unsafe { yaslapi_sys::YASL_peekbool(self.state) }
}
pub fn peek_cstr(&self) -> Option<&str> {
unsafe {
let ptr = yaslapi_sys::YASL_peekcstr(self.state);
if ptr.is_null() {
None
} else {
CStr::from_ptr(ptr).to_str().ok()
}
}
}
pub fn peek_float(&self) -> f64 {
unsafe { yaslapi_sys::YASL_peekfloat(self.state) }
}
pub fn peek_int(&self) -> i64 {
unsafe { yaslapi_sys::YASL_peekint(self.state) }
}
pub fn peek_userdata(&self) -> Option<*mut ::std::os::raw::c_void> {
let ptr = unsafe { yaslapi_sys::YASL_peekuserdata(self.state) };
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn peek_userptr(&self) -> Option<*mut ::std::os::raw::c_void> {
let ptr = unsafe { yaslapi_sys::YASL_peekuserptr(self.state) };
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn peek_type(&self) -> Type {
unsafe { yaslapi_sys::YASL_peektype(self.state) }.into()
}
#[allow(clippy::missing_panics_doc)]
pub fn peek_type_name(&self) -> &str {
unsafe { CStr::from_ptr(yaslapi_sys::YASL_peektypename(self.state)) }
.to_str()
.expect("YASL internal error: YASL_peektypename returned invalid UTF-8")
}
pub fn peek_n_bool(&self, n: usize) -> bool {
unsafe {
yaslapi_sys::YASL_peeknbool(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn peek_n_float(&self, n: usize) -> f64 {
unsafe {
yaslapi_sys::YASL_peeknfloat(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn peek_n_int(&self, n: usize) -> i64 {
unsafe {
yaslapi_sys::YASL_peeknint(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
}
}
pub fn peek_n_userdata(&self, n: usize) -> Option<*mut ::std::os::raw::c_void> {
let ptr = unsafe {
yaslapi_sys::YASL_peeknuserdata(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
)
};
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn peek_n_typename(&self, n: usize) -> &str {
unsafe {
CStr::from_ptr(yaslapi_sys::YASL_peekntypename(
self.state,
n.try_into()
.expect("Index must be able to safely convert into a C unsigned integer."),
))
}
.to_str()
.expect("YASL internal error: YASL_peekntypename returned invalid UTF-8")
}
pub fn peek_vargs_count(&self) -> i64 {
unsafe { yaslapi_sys::YASL_peekvargscount(self.state) }
}
pub fn pop(&mut self) {
unsafe { yaslapi_sys::YASL_pop(self.state) }
}
pub fn pop_bool(&mut self) -> bool {
unsafe { yaslapi_sys::YASL_popbool(self.state) }
}
pub fn pop_str(&mut self) -> Option<&str> {
unsafe {
let ptr = yaslapi_sys::YASL_popcstr(self.state);
if ptr.is_null() {
None
} else {
Some(
CStr::from_ptr(ptr)
.to_str()
.expect("Data was popped at a C-string but contained invalid UTF-8."),
)
}
}
}
pub fn pop_float(&mut self) -> f64 {
unsafe { yaslapi_sys::YASL_popfloat(self.state) }
}
pub fn pop_int(&mut self) -> i64 {
unsafe { yaslapi_sys::YASL_popint(self.state) }
}
pub fn pop_userdata(&mut self) -> Option<*mut ::std::os::raw::c_void> {
let ptr = unsafe { yaslapi_sys::YASL_popuserdata(self.state) };
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn pop_userptr(&mut self) -> Option<*mut ::std::os::raw::c_void> {
let ptr = unsafe { yaslapi_sys::YASL_popuserptr(self.state) };
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn push_bool(&mut self, b: bool) {
unsafe { yaslapi_sys::YASL_pushbool(self.state, b) }
}
pub fn push_cfunction(&mut self, f: CFunction, num_args: i32) {
unsafe { yaslapi_sys::YASL_pushcfunction(self.state, Some(f), num_args) }
}
pub fn push_float(&mut self, f: f64) {
unsafe { yaslapi_sys::YASL_pushfloat(self.state, f) }
}
pub fn push_int(&mut self, i: i64) {
unsafe { yaslapi_sys::YASL_pushint(self.state, i) }
}
pub fn push_list(&mut self) {
unsafe { yaslapi_sys::YASL_pushlist(self.state) }
}
pub fn push_literal(&mut self, string: &'static CStr) {
unsafe { yaslapi_sys::YASL_pushlit(self.state, string.as_ptr().cast()) }
}
pub fn push_table(&mut self) {
unsafe { yaslapi_sys::YASL_pushtable(self.state) }
}
pub fn push_str(&mut self, string: &str) {
unsafe { yaslapi_sys::YASL_pushlstr(self.state, string.as_ptr().cast(), string.len()) }
}
pub fn push_undef(&mut self) {
unsafe { yaslapi_sys::YASL_pushundef(self.state) }
}
pub unsafe fn push_userdata(
&mut self,
data: *mut ::std::os::raw::c_void,
tag: &str,
destructor: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut YASL_State, arg2: *mut ::std::os::raw::c_void),
>,
) {
unsafe { yaslapi_sys::YASL_pushuserdata(self.state, data, tag.as_ptr().cast(), destructor) }
}
pub unsafe fn push_userptr(&mut self, userpointer: *mut ::std::os::raw::c_void) {
unsafe { yaslapi_sys::YASL_pushuserptr(self.state, userpointer) }
}
pub fn push_zstr(&mut self, cstring: &CStr) {
unsafe { yaslapi_sys::YASL_pushzstr(self.state, cstring.as_ptr()) }
}
pub fn register_mt(&mut self, name: &str) -> StateResult {
let name = CString::new(name).unwrap();
unsafe { yaslapi_sys::YASL_registermt(self.state, name.as_ptr()) }.into()
}
pub fn reset_from_script(&mut self, script_location: &str) -> StateResult {
let script_location = CString::new(script_location).unwrap();
unsafe { yaslapi_sys::YASL_resetstate(self.state, script_location.as_ptr()) }.into()
}
pub fn reset_from_source(&mut self, source: &str) -> StateResult {
unsafe { yaslapi_sys::YASL_resetstate_bb(self.state, source.as_ptr().cast(), source.len()) }
.into()
}
pub fn set_global(&mut self, name: &str) -> StateResult {
let name = CString::new(name).unwrap();
unsafe { yaslapi_sys::YASL_setglobal(self.state, name.as_ptr()) }.into()
}
pub fn set_mt(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_setmt(self.state) }.into()
}
pub fn stringify_top(&mut self) {
unsafe { yaslapi_sys::YASL_stringifytop(self.state) }
}
pub fn table_next(&mut self) -> bool {
unsafe { yaslapi_sys::YASL_tablenext(self.state) }
}
pub fn table_set(&mut self) -> StateResult {
unsafe { yaslapi_sys::YASL_tableset(self.state) }.into()
}
pub fn throw_err(&self, error: isize) -> ! {
unsafe {
yaslapi_sys::YASL_throw_err(
self.state,
error
.try_into()
.expect("Error ID must be able to safely convert into a C signed integer."),
)
}
}
}
impl Default for State {
fn default() -> Self {
Self::from_source("")
}
}
impl Drop for State {
fn drop(&mut self) {
let r = unsafe { yaslapi_sys::YASL_delstate(self.state) };
assert_eq!(
StateResult::Success,
num::FromPrimitive::from_i32(r).unwrap()
);
}
}
impl From<i32> for StateResult {
fn from(e: i32) -> Self {
match num::FromPrimitive::from_i32(e) {
Some(r) => r,
None => panic!("Unknown error was returned: {e:?}"),
}
}
}
impl From<StateResult> for i32 {
fn from(e: StateResult) -> Self {
e as Self
}
}
impl From<StateResult> for bool {
fn from(e: StateResult) -> Self {
e.success()
}
}
impl StateResult {
pub fn success(&self) -> bool {
matches!(self, StateResult::Success | StateResult::ModuleSuccess)
}
pub fn failure(&self) -> bool {
!self.success()
}
}
impl From<i32> for Type {
fn from(t: i32) -> Self {
match num::FromPrimitive::from_i32(t) {
Some(r) => r,
None => panic!("Unknown type was returned: {t:?}"),
}
}
}
impl From<Type> for i32 {
fn from(t: Type) -> Self {
t as Self
}
}