#![allow(improper_ctypes_definitions)]
use crate::state::{ExportFnEntry, GrugEntityHandle, GrugInitSettings, GrugState, Files, FileInfo};
use crate::ntstring::NTStrPtr;
use crate::types::{FileId, ExportFnId, GrugEntity, Value, HostFnWithState, HostFnRegErased, INVALID_GRUG_FILE_ID};
use crate::error::{Error, GrugError};
use std::ffi::OsString;
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
type CState = (GrugState, UnsafeCell<Option<Error>>, UnsafeCell<Files>, UnsafeCell<Vec<OsString>>);
#[unsafe(no_mangle)]
pub extern "C" fn grug_default_settings() -> GrugInitSettings<'static> {
GrugInitSettings::new()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_init(settings: GrugInitSettings, out_err: &mut MaybeUninit<GrugError<'static>>) -> Option<Box<CState>> {
match settings.build_state() {
Ok(state) => Some(Box::new((state, UnsafeCell::new(None), UnsafeCell::new(Files::empty()), UnsafeCell::new(vec![])))),
Err(err) => {
unsafe{*out_err.as_mut_ptr() = err.leak()};
None
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_deinit(_: Option<Box<CState>>) {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_register_host_fn<'a>(state: &'a mut CState, fn_name: NTStrPtr, func: HostFnWithState<0, GrugState>) -> Option<&'a GrugError<'a>> {
if let Err(err) = unsafe{state.0.register_host_fn(fn_name.to_str(), func)} {
Some(state.1.get_mut().insert(err).inner())
} else {
None
}
}
pub unsafe extern "C" fn grug_register_method<'a>(state: &'a mut CState, class_name: NTStrPtr, fn_name: NTStrPtr, func: HostFnWithState<0, GrugState>) -> Option<&'a GrugError<'a>> {
if let Err(err) = unsafe{state.0.register_method(class_name.to_str(), fn_name.to_str(), func)} {
Some(state.1.get_mut().insert(err).inner())
} else {
None
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_register_generic_fn<'a>(state: &'a mut CState, fn_name: NTStrPtr, func: HostFnRegErased) -> Option<&'a GrugError<'a>> {
if let Err(err) = unsafe{state.0.register_generic_fn_internal_unsafe(None, fn_name.to_str(), func)} {
Some(state.1.get_mut().insert(err).inner())
} else {
None
}
}
pub unsafe extern "C" fn grug_register_generic_method<'a>(state: &'a mut CState, class_name: NTStrPtr, fn_name: NTStrPtr, func: HostFnRegErased) -> Option<&'a GrugError<'a>> {
if let Err(err) = unsafe{state.0.register_generic_fn_internal_unsafe(Some(class_name.to_str()), fn_name.to_str(), func)} {
Some(state.1.get_mut().insert(err).inner())
} else {
None
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_compile_all_files(state: &CState) -> &[FileInfo<'_>] {
let files = unsafe{&mut *state.2.get()};
*files = state.0.compile_all_files();
files.files()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_update(state: &CState) -> &[FileInfo<'_>] {
let files = unsafe{&mut *state.2.get()};
let resources = unsafe{&mut *state.3.get()};
(*resources, *files) = state.0.update_files();
files.files()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_compile_file(state: &CState, file_path: NTStrPtr<'_>) -> FileId {
match state.0.compile_grug_file(file_path.to_str()) {
Ok(id) => id,
Err(err) => {
unsafe{*state.1.get() = Some(err)};
INVALID_GRUG_FILE_ID
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_create_entity(state: &CState, file_id: FileId) -> Option<GrugEntityHandle<'_>> {
state.0.create_entity(file_id)
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_deinit_entity(state: &CState, handle: GrugEntityHandle<'_>) -> bool {
state.0.destroy_entity(handle)
}
const INVALID_GRUG_EXPORT_FN_ID: ExportFnId = ExportFnId(u64::MAX);
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_fn_ids(state: &CState) -> &[ExportFnEntry<'_>] {
state.0.get_export_fns()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_on_fn_id(state: &CState, entity_type: NTStrPtr<'_>, on_fn_name: NTStrPtr<'_>) -> ExportFnId {
match state.0.get_export_fn_id(entity_type.to_str(), on_fn_name.to_str()) {
Ok(id) => id,
Err(err) => {
unsafe{*state.1.get() = Some(err)};
INVALID_GRUG_EXPORT_FN_ID
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_call_export_fn(state: &CState, entity: &GrugEntity, on_fn_id: ExportFnId, values: *const Value, values_len: usize) -> bool {
unsafe{state.0.call_export_fn(entity, on_fn_id, std::slice::from_raw_parts(values, values_len))}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_set_runtime_error(state: &CState, message: NTStrPtr) {
state.0.set_host_fn_error(message.to_str());
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_all_host_fns_registered(state: &mut CState) -> Option<&GrugError<'_>> {
let Err(err) = state.0.all_host_fns_registered() else {
return None
};
Some(state.1.get_mut().insert(err).inner())
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_error<'a>(state: &'a CState) -> Option<&'a GrugError<'a>> {
Some(unsafe{&*state.1.get()}.as_ref()?.inner())
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_entity_get_data<'a>(_: &CState, entity: Option<&'a GrugEntity>) -> Option<&'a GrugEntity> {
entity
}