use core::{cell::Cell, cmp::Ordering, ffi::c_void, fmt, marker::PhantomData};
pub use evil_janet::JanetAbstractType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AbstractError {
MismatchedSize,
MismatchedAbstractType,
NullDataPointer,
}
impl fmt::Display for AbstractError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MismatchedSize => f.pad("Mismatched size between requested type and actual type"),
Self::MismatchedAbstractType => {
f.pad("Mismatched fn pointers between requested type and actual type")
},
Self::NullDataPointer => f.pad("Data pointer is NULL"),
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(_doc, doc(cfg(feature = "std")))]
impl std::error::Error for AbstractError {}
#[derive(PartialEq, Eq)]
#[repr(transparent)]
pub struct JanetAbstract {
pub(crate) raw: *mut c_void,
phantom: PhantomData<Cell<()>>,
}
impl JanetAbstract {
#[inline]
pub fn new<A: IsJanetAbstract>(value: A) -> Self {
let mut s = Self {
raw: unsafe { evil_janet::janet_abstract(A::type_info(), A::SIZE as _) },
phantom: PhantomData,
};
*unsafe { s.get_mut_unchecked() } = value;
s
}
#[inline]
pub unsafe fn from_raw(raw: *mut c_void) -> Self {
Self {
raw,
phantom: PhantomData,
}
}
#[inline]
pub unsafe fn get_unchecked<A: IsJanetAbstract>(&self) -> &A {
&*(self.raw as *const A)
}
#[inline]
pub unsafe fn get_mut_unchecked<A: IsJanetAbstract>(&mut self) -> &mut A {
&mut *(self.raw as *mut A)
}
#[inline]
pub fn is<A: IsJanetAbstract>(&self) -> bool {
if self.size() != A::SIZE {
return false;
}
let ty_self = self.type_info();
let ty_a = A::type_info();
if ty_self.call != ty_a.call
|| ty_self.compare != ty_a.compare
|| ty_self.tostring != ty_a.tostring
|| ty_self.marshal != ty_a.marshal
|| ty_self.unmarshal != ty_a.unmarshal
|| ty_self.hash != ty_a.hash
|| ty_self.next != ty_a.next
|| ty_self.put != ty_a.put
|| ty_self.get != ty_a.get
|| ty_self.gc != ty_a.gc
|| ty_self.gcmark != ty_a.gcmark
{
return false;
}
true
}
fn check<A: IsJanetAbstract>(&self) -> Result<(), AbstractError> {
if self.size() != A::SIZE {
return Err(AbstractError::MismatchedSize);
}
let ty_self = self.type_info();
let ty_a = A::type_info();
if ty_self.call != ty_a.call
|| ty_self.compare != ty_a.compare
|| ty_self.tostring != ty_a.tostring
|| ty_self.marshal != ty_a.marshal
|| ty_self.unmarshal != ty_a.unmarshal
|| ty_self.hash != ty_a.hash
|| ty_self.next != ty_a.next
|| ty_self.put != ty_a.put
|| ty_self.get != ty_a.get
|| ty_self.gc != ty_a.gc
|| ty_self.gcmark != ty_a.gcmark
{
return Err(AbstractError::MismatchedAbstractType);
}
Ok(())
}
#[inline]
pub fn get<A: IsJanetAbstract>(&self) -> Result<&A, AbstractError> {
self.check::<A>()?;
let ptr = self.raw as *const A;
if ptr.is_null() {
return Err(AbstractError::NullDataPointer);
}
Ok(unsafe { &*ptr })
}
#[inline]
pub fn get_mut<A: IsJanetAbstract>(&mut self) -> Result<&mut A, AbstractError> {
self.check::<A>()?;
let ptr = self.raw as *mut A;
if ptr.is_null() {
return Err(AbstractError::NullDataPointer);
}
Ok(unsafe { &mut *(ptr) })
}
#[allow(clippy::wrong_self_convention)]
#[inline]
pub const fn as_raw(&self) -> *const c_void {
self.raw
}
#[allow(clippy::wrong_self_convention)]
#[inline]
pub fn as_mut_raw(&mut self) -> *mut c_void {
self.raw
}
#[inline]
pub fn cast<U: IsJanetAbstract>(self) -> *mut U {
self.raw as *mut U
}
#[inline]
pub fn size(&self) -> usize {
unsafe { (*evil_janet::janet_abstract_head(self.raw)).size as usize }
}
#[inline]
pub fn type_info(&self) -> JanetAbstractType {
unsafe { *(*evil_janet::janet_abstract_head(self.raw)).type_ }
}
#[inline]
fn raw_type_info(&self) -> *const JanetAbstractType {
unsafe { (*evil_janet::janet_abstract_head(self.raw)).type_ }
}
}
impl fmt::Debug for JanetAbstract {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JanetAbstract")
.field("mem_size", &self.size())
.finish()
}
}
impl PartialOrd for JanetAbstract {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.raw == other.raw {
return Some(Ordering::Equal);
}
let self_ty = self.raw_type_info();
let other_ty = self.raw_type_info();
if self_ty != other_ty && self_ty > other_ty {
return Some(Ordering::Greater);
}
let self_ty = self.type_info();
if let Some(f) = self_ty.compare {
let res = unsafe { f(self.raw, other.raw) };
match res {
-1 => Some(Ordering::Less),
0 => Some(Ordering::Equal),
1 => Some(Ordering::Greater),
_ => None,
}
} else if self.raw > other.raw {
Some(Ordering::Greater)
} else {
Some(Ordering::Less)
}
}
}
impl Ord for JanetAbstract {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
if self.raw == other.raw {
return Ordering::Equal;
}
let self_ty = self.raw_type_info();
let other_ty = self.raw_type_info();
if self_ty != other_ty && self_ty > other_ty {
return Ordering::Greater;
}
let self_ty = self.type_info();
if let Some(f) = self_ty.compare {
let res = unsafe { f(self.raw, other.raw) };
match res {
-1 => Ordering::Less,
0 => Ordering::Equal,
1 => Ordering::Greater,
_ => unreachable!(),
}
} else if self.raw > other.raw {
Ordering::Greater
} else {
Ordering::Less
}
}
}
pub trait IsJanetAbstract {
const SIZE: usize;
fn type_info() -> &'static JanetAbstractType;
}
pub fn register<T: IsJanetAbstract>() {
let at = T::type_info();
unsafe {
let syn = evil_janet::janet_wrap_symbol(evil_janet::janet_csymbol(at.name));
let abs_type_ptr = evil_janet::janet_get_abstract_type(syn);
if abs_type_ptr.is_null() {
evil_janet::janet_register_abstract_type(at);
}
}
}
impl IsJanetAbstract for i64 {
const SIZE: usize = core::mem::size_of::<Self>();
#[inline]
fn type_info() -> &'static JanetAbstractType {
unsafe { &evil_janet::janet_s64_type }
}
}
impl IsJanetAbstract for u64 {
const SIZE: usize = core::mem::size_of::<Self>();
#[inline]
fn type_info() -> &'static JanetAbstractType {
unsafe { &evil_janet::janet_u64_type }
}
}
#[cfg(all(test, any(feature = "amalgation", feature = "link-system")))]
mod tests {
use super::*;
#[test]
fn creation_and_getting_value() -> Result<(), crate::client::Error> {
let _client = crate::client::JanetClient::init()?;
let test = JanetAbstract::new(10u64);
let test2 = JanetAbstract::new(12i64);
let val = test.get::<u64>();
let val2 = test2.get::<i64>();
let val3 = test.get::<i64>();
assert_eq!(Ok(&10), val);
assert_eq!(Ok(&12), val2);
assert_eq!(Err(AbstractError::MismatchedAbstractType), val3);
Ok(())
}
#[test]
fn get_mut() -> Result<(), crate::client::Error> {
let _client = crate::client::JanetClient::init()?;
let mut test = JanetAbstract::new(10u64);
let mut test2 = JanetAbstract::new(12i64);
let val = test.get_mut::<u64>();
let val2 = test2.get_mut::<i64>();
assert_eq!(Ok(&mut 10), val);
assert_eq!(Ok(&mut 12), val2);
if let Ok(val) = val {
*val = 1000;
assert_eq!(&mut 1000, val);
}
if let Ok(val2) = val2 {
*val2 = 2000;
assert_eq!(&mut 2000, val2);
}
Ok(())
}
#[test]
fn size() -> Result<(), crate::client::Error> {
let _client = crate::client::JanetClient::init()?;
let test = JanetAbstract::new(10u64);
let test2 = JanetAbstract::new(12i64);
assert_eq!(u64::SIZE, test.size());
assert_eq!(u64::SIZE, test2.size());
Ok(())
}
}