use crate::xar::XarHandle;
use crate::mod_api::{ModApi, get_mod_api, get_mod_api_from_text};
use crate::error::{Error, ErrorKind, SourceSpan};
use crate::backend::{Backend, ErasedBackend, BytecodeBackend};
use crate::types::{Value, Id, HostFnWithState, HostFnReg, HostFnRegErased, ExportFnId, FileId, GrugEntity, INVALID_GRUG_FILE_ID};
use crate::xar::Xar;
use crate::ntstring::{NTStrPtr};
use crate::arena::Arena;
use crate::own_ptr::OwnPtr;
use crate::nt;
use crate::watcher::watch_changes;
use crate::type_storage::TypeStorage;
use gruggers_core::runtime_error::RuntimeError;
pub use gruggers_core::state::State;
pub use gruggers_core::ast::GrugAst;
use std::path::{Path, PathBuf};
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::cell::{Cell, RefCell, Ref};
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicU64, Ordering};
use std::ffi::{OsString, OsStr};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::sync::Arc;
#[repr(C)]
pub struct RuntimeErrorHandler {
data: NonNull<()>,
drop: Option<extern "C" fn(data: Option<NonNull<()>>)>,
func: Option<for <'a> extern "C" fn(
data: NonNull<()>,
error: &'a RuntimeError<'a>,
)>,
}
const _: () = const {
assert!(std::mem::size_of::<RuntimeErrorHandler>() == std::mem::size_of::<Option<RuntimeErrorHandler>>());
};
impl RuntimeErrorHandler {
pub const fn new_default () -> Self {
Self {
data: NonNull::dangling(),
drop: None,
func: None
}
}
fn handle_error(&self, error: &RuntimeError) {
if let Some(func) = self.func {
func(
self.data,
error
)
}
}
}
impl Default for RuntimeErrorHandler {
fn default() -> Self {
Self::new_default()
}
}
impl<F: for<'b> Fn(&RuntimeError)> From<F> for RuntimeErrorHandler {
fn from(f: F) -> Self {
let f = unsafe{NonNull::new_unchecked(Box::into_raw(Box::new(f)))}.cast::<()>();
extern "C" fn handler<F: Fn(&RuntimeError)> (
data: NonNull<()>,
error: &RuntimeError,
) {
unsafe{(data.cast::<F>().as_ref())(
error
)};
}
extern "C" fn drop<F>(data: Option<NonNull<()>>) {
data.map(|x| unsafe{Box::from_raw(x.cast::<F>().as_ptr())});
}
Self {
data: f,
drop: Some(drop::<F> as extern "C" fn(_)),
func: Some(handler::<F> as for <'a> extern "C" fn(NonNull<()>, &'a RuntimeError<'a>)),
}
}
}
#[repr(C)]
pub struct GrugInitSettings<'a> {
_marker: PhantomData<&'a ()>,
mod_api_path: Option<NonNull<u8>>,
mod_api_path_len: usize,
mods_dir_path: Option<NonNull<u8>>,
mods_dir_path_len: usize,
runtime_error_handler: Option<RuntimeErrorHandler>,
backend: Option<ErasedBackend<GrugState>>,
}
const _: () = const {
unsafe{std::mem::forget(std::mem::MaybeUninit::<GrugInitSettings<'static>>::zeroed().assume_init())};
};
impl<'a> GrugInitSettings<'a> {
pub const fn new() -> Self {
Self {
_marker: PhantomData,
mod_api_path: None,
mod_api_path_len: 0,
mods_dir_path: None,
mods_dir_path_len: 0,
runtime_error_handler: None,
backend: None,
}
}
pub fn set_mods_dir<P: AsRef<OsStr> + ?Sized>(mut self, dir: &'a P) -> Self {
let dir = dir.as_ref();
if dir.is_empty() {
self.mods_dir_path = None;
self.mods_dir_path_len = 0;
} else {
self.mods_dir_path = Some(NonNull::from_ref(dir).cast::<u8>());
self.mods_dir_path_len = dir.len();
}
self
}
pub fn set_mod_api_path<P: AsRef<OsStr> + ?Sized>(mut self, mod_api: &'a P) -> Self {
let mod_api = mod_api.as_ref();
if mod_api.is_empty() {
self.mod_api_path = None;
self.mod_api_path_len = 0;
} else {
self.mod_api_path = Some(NonNull::from_ref(mod_api).cast::<u8>());
self.mod_api_path_len = mod_api.len();
}
self
}
pub fn set_backend<B: Backend>(mut self, backend: B) -> Self {
self.backend = Some(backend.into());
self
}
pub fn set_runtime_error_handler<F: for<'b> Fn(&RuntimeError)> (mut self, f: F) -> Self {
self.runtime_error_handler = Some(f.into());
self
}
pub fn build_state(self) -> Result<GrugState, Error> {
let mod_api_path = unsafe{Self::maybe_nt_or_length(self.mod_api_path, self.mod_api_path_len)}
.unwrap_or("./mod_api.json");
let mods_dir_path = unsafe{Self::maybe_nt_or_length(self.mods_dir_path, self.mods_dir_path_len)}
.unwrap_or("./mods");
GrugState::new(
mod_api_path,
mods_dir_path,
self.runtime_error_handler.unwrap_or_else(RuntimeErrorHandler::new_default),
self.backend.unwrap_or_else(|| BytecodeBackend::new().into())
)
}
unsafe fn maybe_nt_or_length(ptr: Option<NonNull<u8>>, len: usize) -> Option<&'a str> {
if let Some(ptr) = ptr {
if len == 0 {
let mut i = 0;
loop {
if unsafe{ptr.add(i).read()} == b'\0' {
return Some(
unsafe{std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr.as_ptr(), i))}
)
}
i += 1;
}
} else {
Some(
unsafe{std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr.as_ptr(), len))}
)
}
} else {None}
}
}
impl Default for GrugInitSettings<'static> {
fn default () -> Self {
Self::new()
}
}
pub fn default_runtime_error_handler(_err_kind: u32, reason: &str, on_fn_name: &str, script_path: &str) {
println!("Runtime Error: {} in function {} in script {}", reason, on_fn_name, script_path);
std::process::exit(1);
}
pub struct GrugState {
pub(crate) mod_api: Arc<ModApi>,
pub(crate) mods_dir_path: OsString,
pub(crate) type_storage: RefCell<TypeStorage>,
next_entity_id: AtomicU64,
pub(crate) runtime_error_handler: RuntimeErrorHandler,
pub(crate) script_entities: RefCell<Vec<Vec<NonNull<GrugEntity>>>>,
pub(crate) entities: Xar<GrugEntity>,
pub(crate) resources: RefCell<HashSet<OsString>>,
pub(crate) compiler_senders: Vec<Sender<(Arena, &'static [&'static OsStr])>>,
pub(crate) compiler_receiver: Receiver<(Arena, OwnPtr<'static, [(Result<GrugAst<'static>, Error>, &'static OsStr)]>, &'static [&'static OsStr])>,
export_functions: Vec<ExportFnEntry<'static>>,
pub(crate) path_to_script_ids: RefCell<HashMap<OsString, (OsString, FileId)>>,
next_script_id: AtomicU64,
pub(crate) backend: ErasedBackend<Self>,
pub(crate) arenas : RefCell<Vec<Arena>>,
pub(crate) is_errorring: Cell<bool>,
pub(crate) changes: Receiver<Result<OsString, std::io::Error>>,
}
impl State for GrugState {
fn handle_runtime_error(&self, error: &RuntimeError) {
self.is_errorring.set(true);
self.runtime_error_handler.handle_error(
error,
);
}
}
impl GrugState {
fn new (mod_api_path: impl AsRef<OsStr>, mods_dir_path: impl AsRef<OsStr>, handler: RuntimeErrorHandler, backend: ErasedBackend<Self>) -> Result<Self, Error> {
let mod_api = get_mod_api(mod_api_path.as_ref())?;
Self::new_inner(mod_api, mods_dir_path, handler, backend)
}
pub fn new_from_text (mod_api_text: &str, mods_dir_path: impl AsRef<OsStr>, handler: RuntimeErrorHandler, backend: impl Into<ErasedBackend<Self>>) -> Result<Self, Error> {
let mod_api = get_mod_api_from_text("<Mod API Source>", mod_api_text)?;
Self::new_inner(mod_api, mods_dir_path, handler, backend.into())
}
fn new_inner (mod_api: ModApi, mods_dir_path: impl AsRef<OsStr>, handler: RuntimeErrorHandler, backend: ErasedBackend<Self>) -> Result<Self, Error> {
let mut on_fns = Vec::new();
let init_globals = nt!("init_globals");
let mods_dir_path = PathBuf::from(mods_dir_path.as_ref());
for (entity_type, entity) in mod_api.entities() {
on_fns.push(ExportFnEntry {
entity_type : unsafe{entity_type.as_ntstrptr().detach_lifetime()},
fn_name : unsafe{init_globals.as_ntstrptr().detach_lifetime()},
index : 0,
});
for (i, (fn_name, _)) in entity.export_fns.iter().enumerate() {
on_fns.push(ExportFnEntry{
entity_type : unsafe{entity_type.as_ntstrptr().detach_lifetime()},
fn_name : unsafe{fn_name.as_ntstrptr().detach_lifetime()},
index : i,
});
}
}
let mod_api = Arc::new(mod_api);
let (sender, reciever) = channel();
watch_changes(&mods_dir_path, move |changes| sender.send(changes).is_ok()).unwrap();
let num_threads = {
let available_threads = std::thread::available_parallelism().map(|x| x.get()).unwrap_or(1);
if available_threads <= 2 {1} else {available_threads - 2}
};
let (snd, rcv) = channel();
let compiler_senders = (0..num_threads).map(|_| {
let (per_thread_send, per_thread_rcv) = channel();
std::thread::spawn(Self::compiler_thread_fn(
per_thread_rcv,
snd.clone(),
mods_dir_path.clone(),
Arc::clone(&mod_api),
));
per_thread_send
}).collect::<Vec<_>>();
Ok(Self {
mod_api,
mods_dir_path: mods_dir_path.into(),
type_storage: RefCell::new(TypeStorage::new()),
next_entity_id: AtomicU64::new(0),
runtime_error_handler: handler,
resources: RefCell::new(HashSet::new()),
script_entities: RefCell::new(Vec::new()),
entities: Xar::new(),
compiler_senders,
compiler_receiver: rcv,
export_functions: on_fns,
path_to_script_ids: RefCell::new(HashMap::new()),
next_script_id: AtomicU64::new(0),
arenas: RefCell::new(Vec::new()),
backend,
is_errorring: Cell::new(false),
changes: reciever,
})
}
pub(crate) fn get_or_insert_script_id(&self, path: &Path) -> FileId {
let mut canonicalized = PathBuf::from(self.mods_dir_path.clone());
canonicalized.push(path);
let canonicalized = canonicalized.canonicalize().expect("error while canonicalizing");
let mut path_to_script_ids = self.path_to_script_ids.borrow_mut();
match path_to_script_ids.get(canonicalized.as_os_str()) {
Some((_, id)) => *id,
None => {
let id = self.get_next_script_id();
assert!(path_to_script_ids.insert(canonicalized.into_os_string(), (OsString::from(path), id)).is_none());
id
}
}
}
pub fn mods_dir_path(&self) -> &OsStr {
self.mods_dir_path.as_ref()
}
pub fn get_export_fn_id(&self, entity_type: &str, fn_name: &str) -> Result<ExportFnId, Error> {
if !self.mod_api.entities().contains_key(entity_type) {
return Err(Error::new(
ErrorKind::INIT_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("mod api does not define an entity named {}", entity_type),
));
}
for (i, on_fn_entry) in self.export_functions.iter().enumerate() {
if on_fn_entry.entity_type() == entity_type && on_fn_entry.fn_name() == fn_name {
return Ok(ExportFnId(i as u64))
}
}
return Err(Error::new(
ErrorKind::INIT_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("'{}' does not export a function named '{}'", entity_type, fn_name),
));
}
pub fn get_export_fn_name(&self, fn_id: ExportFnId) -> Option<&str> {
self.export_functions.get(fn_id.0 as usize).map(|entry| entry.fn_name())
}
pub fn get_export_fns(&self) -> &[ExportFnEntry<'_>] {
&self.export_functions
}
pub fn get_entity_export_functions(&self, entity_type: &str) -> Result<&[ExportFnEntry<'_>], Error> {
if !self.mod_api.entities().contains_key(entity_type) {
return Err(Error::new(
ErrorKind::INIT_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("mod api does not define an entity named {}", entity_type),
));
}
let mut start = 0;
while start != self.export_functions.len() && self.export_functions[start].entity_type() != entity_type {
start += 1;
}
let mut end = start;
while end != self.export_functions.len() && self.export_functions[end].entity_type() == entity_type {
end += 1;
}
Ok(&self.export_functions[start..end])
}
pub fn get_script_path_rel(&self, script_id: FileId) -> Option<&OsStr> {
let string = Ref::filter_map(self.path_to_script_ids.borrow(), |inner|
inner.values().find(|(_, v)| *v == script_id).map(|x| &*x.0)
).ok()?;
let string: &OsStr = unsafe{&*(&*string as *const OsStr)};
Some(string)
}
pub fn all_host_fns_registered(&self) -> Result<(), Error> {
for (host_fn_name, host_fn) in self.mod_api.host_fns() {
if let None = host_fn.fn_ptr && let None = host_fn.registerer {
return Err(Error::new(
ErrorKind::INIT_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("host function '{host_fn_name}' has not been registered"),
));
}
}
for (class_name, class) in self.mod_api.classes() {
for (method_name, method) in &*class.methods {
if let None = method.fn_ptr && let None = method.registerer {
return Err(Error::new(
ErrorKind::INIT_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("method '{method_name}' in class '{class_name}' has not been registered"),
));
}
}
}
Ok(())
}
pub(crate) fn get_next_script_id(&self) -> FileId {
Id::new(self.next_script_id.fetch_add(1, Ordering::Relaxed))
}
pub fn get_next_entity_id(&self) -> Id {
Id::new(self.next_entity_id.fetch_add(1, Ordering::Relaxed))
}
pub unsafe fn set_next_entity_id(&self, next_id: u64) {
self.next_entity_id.store(next_id, Ordering::Relaxed);
}
pub fn create_entity(&self, file_id: FileId) -> Option<GrugEntityHandle<'_>> {
let entity = self.entities.insert(unsafe{GrugEntity::new_uninit(self.get_next_entity_id(), file_id)});
let entity = unsafe{GrugEntityHandle::new(entity)};
let success = self.backend.init_entity(self, &entity);
if success {
self.script_entities.borrow_mut().get_mut(file_id.to_inner() as usize)
.expect("script must already exist")
.push(NonNull::from_ref(&*entity));
Some(entity)
} else {
unsafe{self.entities.delete(entity.into_inner());}
None
}
}
pub fn destroy_entity<'a>(&'a self, entity: GrugEntityHandle<'a>) -> bool {
if self.entities.contains(entity.0) {
unsafe{self.backend.destroy_entity_data(&entity);}
self.script_entities.borrow_mut().get_mut(entity.file_id.to_inner() as usize)
.expect("script must already exist")
.extract_if(.., |item| {
*item == NonNull::from_ref(&*entity)
}).for_each(|_| {});
unsafe{self.entities.delete(entity.into_inner())};
true
} else {
false
}
}
pub fn clear_entities(&mut self) {
self.backend.clear_entities();
self.script_entities.borrow_mut().clear();
self.entities.clear();
}
pub fn clear_error(&self) {
self.is_errorring.set(false);
}
fn get_export_fn_index(&self, id: ExportFnId) -> usize {
self.export_functions[id.0 as usize].index
}
pub fn set_host_fn_error(&self, message: &str) {
self.backend.raise_runtime_error(self, message);
}
}
impl GrugState {
pub unsafe fn register_host_fn<const N: usize>(&mut self, fn_name: &str, func: HostFnWithState<N, Self>) -> Result<(), Error> {
unsafe{self.register_host_fn_internal(None, fn_name, func)}
}
pub unsafe fn register_method<const N: usize>(&mut self, class_name: &str, fn_name: &str, func: HostFnWithState<N, Self>) -> Result<(), Error> {
unsafe{self.register_host_fn_internal(Some(class_name), fn_name, func)}
}
unsafe fn register_host_fn_internal<const N: usize>(&mut self, class_name: Option<&str>, fn_name: &str, func: HostFnWithState<N, Self>) -> Result<(), Error> {
let mod_api = *unsafe{std::mem::transmute::<&mut Arc<ModApi>, &mut *mut u8>(&mut self.mod_api)};
let mod_api = unsafe{mod_api.byte_add(16).cast::<ModApi>()};
unsafe{(&mut *mod_api).register_fn(class_name, fn_name, func)}
}
pub unsafe fn register_generic_fn<const N: usize>(&mut self, fn_name: &str, func: HostFnReg<N, Self>) -> Result<(), Error> {
unsafe{self.register_generic_fn_internal(None, fn_name, func)}
}
pub unsafe fn register_generic_method<const N: usize>(&mut self, class_name: &str, fn_name: &str, func: HostFnReg<N, Self>) -> Result<(), Error> {
unsafe{self.register_generic_fn_internal(Some(class_name), fn_name, func)}
}
unsafe fn register_generic_fn_internal<const N: usize>(&mut self, class_name: Option<&str>, fn_name: &str, func: HostFnReg<N, Self>) -> Result<(), Error> {
let mod_api = *unsafe{std::mem::transmute::<&mut Arc<ModApi>, &mut *mut u8>(&mut self.mod_api)};
let mod_api = unsafe{mod_api.byte_add(16).cast::<ModApi>()};
unsafe{(&mut *mod_api).register_generic_fn(class_name, fn_name, func)}
}
pub(crate) unsafe fn register_generic_fn_internal_unsafe(&mut self, class_name: Option<&str>, fn_name: &str, func: HostFnRegErased) -> Result<(), Error> {
let mod_api = *unsafe{std::mem::transmute::<&mut Arc<ModApi>, &mut *mut u8>(&mut self.mod_api)};
let mod_api = unsafe{mod_api.byte_add(16).cast::<ModApi>()};
unsafe{(&mut *mod_api).register_generic_fn_unchecked(class_name, fn_name, func)}
}
pub unsafe fn register_dummies(&mut self) {
let mod_api = *unsafe{std::mem::transmute::<&mut Arc<ModApi>, &mut *mut u8>(&mut self.mod_api)};
let mod_api = unsafe{mod_api.byte_add(16).cast::<ModApi>()};
unsafe{(&mut *mod_api).register_dummies()}
}
}
impl GrugState {
#[must_use]
pub unsafe fn call_export_fn_raw(&self, entity: &GrugEntity, fn_id: ExportFnId, values: *const Value) -> bool {
let ret_val = unsafe {
self.backend.call_on_function_raw(self, entity, self.get_export_fn_index(fn_id), values)
};
ret_val
}
#[must_use]
pub fn call_export_fn(&self, entity: &GrugEntity, fn_id: ExportFnId, values: &[Value]) -> bool {
let ret_val = self.backend.call_on_function(self, entity, self.get_export_fn_index(fn_id), values);
ret_val
}
}
pub struct ExportFnEntry<'a> {
entity_type : NTStrPtr<'a>,
fn_name : NTStrPtr<'a>,
pub index : usize,
}
impl<'a> ExportFnEntry<'a> {
pub fn entity_type(&self) -> &str {
self.entity_type.to_str()
}
pub fn fn_name(&self) -> &str {
self.fn_name.to_str()
}
}
const _: () = const{
let x: &[ExportFnEntry] = &[];
unsafe{assert!(x.len() == (&x as *const _ as *const usize).add(1).read());}
};
#[repr(transparent)]
pub struct GrugEntityHandle<'a>(XarHandle<'a, GrugEntity>);
impl<'a> GrugEntityHandle<'a> {
pub unsafe fn new(inner: XarHandle<'a, GrugEntity>) -> Self {
Self(inner)
}
pub fn into_inner(self) -> XarHandle<'a, GrugEntity> {
self.0
}
}
impl<'a> AsRef<GrugEntity> for GrugEntityHandle<'a> {
fn as_ref(&self) -> &GrugEntity {
unsafe{self.0.get_ref()}
}
}
impl<'a> std::ops::Deref for GrugEntityHandle<'a> {
type Target = GrugEntity;
fn deref(&self) -> &Self::Target {
unsafe{self.0.get_ref()}
}
}
mod files {
use crate::own_ptr::OwnPtr;
use crate::arena::Arena;
use crate::ntstring::{NTBytes, NTStrPtr};
use crate::types::FileId;
use crate::error::GrugError;
use crate::state::INVALID_GRUG_FILE_ID;
use std::ffi::OsStr;
use std::path::Path;
use std::mem::MaybeUninit;
pub struct Files {
pub(crate) inner: OwnPtr<'static, [FileInfo<'static>]>,
pub(crate) _arena: Arena,
}
impl std::fmt::Debug for Files {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.files().fmt(f)
}
}
impl Files {
pub fn empty() -> Self {
Self {
inner: (Box::new([]) as Box<[_]>).into(),
_arena: Arena::new(),
}
}
pub fn files<'a>(&'a self) -> &'a [FileInfo<'a>] {
&*self.inner
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct FileInfo<'a> {
pub(crate) path: NTBytes<'a>,
pub(crate) file_name: NTBytes<'a>,
pub(crate) mod_name: NTBytes<'a>,
pub(crate) entity_type: NTStrPtr<'a>,
pub(crate) entity_name: NTBytes<'a>,
pub(crate) file_id: FileId,
pub(crate) error: MaybeUninit<GrugError<'a>>,
}
impl<'a> FileInfo<'a> {
pub(crate) fn new_in(path: &OsStr, file_name: &OsStr, mod_name: &OsStr, entity_type: &str, entity_name: &OsStr, result: Result<FileId, GrugError>, arena: &'a Arena) -> Self {
let path = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(path.as_encoded_bytes()))};
let file_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(file_name.as_encoded_bytes()))};
let mod_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(mod_name.as_encoded_bytes()))};
let entity_type = arena.copy_str_into_nt(entity_type).as_ntstrptr();
let entity_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(entity_name.as_encoded_bytes()))};
let (file_id, error) = match result {
Ok(id) => (id, MaybeUninit::uninit()),
Err(err) => (INVALID_GRUG_FILE_ID, MaybeUninit::new(err.copy_into(arena)))
};
FileInfo {
path,
file_name,
mod_name,
entity_type,
entity_name,
file_id,
error
}
}
pub fn copy_into<'b>(&self, arena: &'b Arena) -> FileInfo<'b> {
let path = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(self.path.to_bytes()))};
let file_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(self.file_name.to_bytes()))};
let mod_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(self.mod_name.to_bytes()))};
let entity_type = arena.copy_str_into_nt(self.entity_type.to_str()).as_ntstrptr();
let entity_name = unsafe{NTBytes::from_bytes_unchecked(arena.copy_bytes_into_nt(self.entity_name.to_bytes()))};
let (file_id, error) = if self.file_id == INVALID_GRUG_FILE_ID {
(INVALID_GRUG_FILE_ID, MaybeUninit::new(unsafe{self.error.assume_init()}.copy_into(arena)))
} else {
(self.file_id, MaybeUninit::uninit())
};
FileInfo {
path,
file_name,
mod_name,
entity_type,
entity_name,
file_id,
error
}
}
pub fn path (&self) -> &Path {
OsStr::as_ref(unsafe{OsStr::from_encoded_bytes_unchecked(self.path.to_bytes())})
}
pub fn file_name (&self) -> &OsStr {
unsafe{OsStr::from_encoded_bytes_unchecked(self.file_name.to_bytes())}
}
pub fn mod_name (&self) -> &OsStr {
unsafe{OsStr::from_encoded_bytes_unchecked(self.mod_name.to_bytes())}
}
pub fn entity_type (&self) -> &str {
self.entity_type.to_str()
}
pub fn entity_name (&self) -> &OsStr {
unsafe{OsStr::from_encoded_bytes_unchecked(self.entity_name.to_bytes())}
}
pub fn result (&self) -> Result<FileId, GrugError<'_>> {
if self.file_id == INVALID_GRUG_FILE_ID {unsafe{Err(*self.error.assume_init_ref())}}
else {Ok(self.file_id)}
}
}
}
pub use files::*;