use crate::{FunctionDefinition, TypeInfo};
use std::{ffi::CStr, os::raw::c_char, slice, str};
#[repr(C)]
pub struct ModuleInfo {
pub(crate) path: *const c_char,
pub(crate) functions: *const FunctionDefinition,
pub(crate) types: *const *const TypeInfo,
pub num_functions: u32,
pub num_types: u32,
}
impl ModuleInfo {
pub fn path(&self) -> &str {
unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.path).to_bytes()) }
}
pub fn functions(&self) -> &[FunctionDefinition] {
if self.num_functions == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.functions, self.num_functions as usize) }
}
}
pub fn types(&self) -> &[&TypeInfo] {
if self.num_types == 0 {
&[]
} else {
unsafe {
slice::from_raw_parts(self.types.cast::<&TypeInfo>(), self.num_types as usize)
}
}
}
}
unsafe impl Send for ModuleInfo {}
unsafe impl Sync for ModuleInfo {}
#[cfg(test)]
mod tests {
use crate::{
test_utils::{
fake_fn_prototype, fake_module_info, fake_struct_info, fake_type_info, FAKE_FN_NAME,
FAKE_MODULE_PATH, FAKE_STRUCT_NAME, FAKE_TYPE_NAME,
},
FunctionDefinition, TypeInfo, TypeInfoData,
};
use std::{ffi::CString, ptr};
#[test]
fn test_module_info_path() {
let module_path = CString::new(FAKE_MODULE_PATH).expect("Invalid fake module path.");
let module = fake_module_info(&module_path, &[], &[]);
assert_eq!(module.path(), FAKE_MODULE_PATH);
}
#[test]
fn test_module_info_types_none() {
let functions = &[];
let types = &[];
let module_path = CString::new(FAKE_MODULE_PATH).expect("Invalid fake module path.");
let module = fake_module_info(&module_path, functions, types);
assert_eq!(module.functions().len(), functions.len());
assert_eq!(module.types().len(), types.len());
}
#[test]
fn test_module_info_types_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_info = fake_type_info(&type_name, 1, 1, TypeInfoData::Primitive);
let return_type = Some(&type_info);
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], return_type);
let fn_info = FunctionDefinition {
prototype: fn_prototype,
fn_ptr: ptr::null(),
};
let functions = &[fn_info];
let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name");
let struct_info = fake_struct_info(&[], &[], &[], Default::default());
let type_info = fake_type_info(&struct_name, 1, 1, TypeInfoData::Struct(struct_info));
let types = &[&type_info];
let module_path = CString::new(FAKE_MODULE_PATH).expect("Invalid fake module path.");
let module = fake_module_info(&module_path, functions, types);
let result_functions = module.functions();
assert_eq!(result_functions.len(), functions.len());
for (lhs, rhs) in result_functions.iter().zip(functions.iter()) {
assert_eq!(lhs.fn_ptr, rhs.fn_ptr);
assert_eq!(lhs.prototype.name(), rhs.prototype.name());
assert_eq!(
lhs.prototype.signature.arg_types(),
rhs.prototype.signature.arg_types()
);
assert_eq!(
lhs.prototype.signature.return_type(),
rhs.prototype.signature.return_type()
);
}
let result_types: &[&TypeInfo] = module.types();
assert_eq!(result_types.len(), types.len());
for (lhs, rhs) in result_types.iter().zip(types.iter()) {
assert_eq!(lhs, rhs);
assert_eq!(lhs.name(), rhs.name());
assert_eq!(lhs.data.is_struct(), rhs.data.is_struct());
assert_eq!(lhs.data.is_primitive(), rhs.data.is_primitive());
if let TypeInfoData::Struct(lhs) = &lhs.data {
if let TypeInfoData::Struct(rhs) = &rhs.data {
assert_eq!(lhs.field_types(), rhs.field_types());
} else {
assert!(false);
}
}
}
}
}