use crate::info::*;
use crate::reflect::*;
use crate::utils::StaticTypeMap;
use crate::{Error, Value};
use core::fmt;
use core::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::lazy::SyncOnceCell;
use std::sync::RwLock;
macro impl_common($ty:ty) {
impl CommonTypeInfo for $ty {
fn name(&self) -> String {
(self.vtable.name)()
}
fn assoc_fns(&self) -> Vec<AssocFn> {
(self.vtable.assoc_fns)()
}
fn assoc_consts(&self) -> Vec<AssocConst> {
(self.vtable.assoc_consts)()
}
fn as_ref<'a>(&self, val: &'a Value) -> Result<Value<'a>, Error> {
(self.vtable.as_ref)(val)
}
fn as_mut<'a>(&self, val: &'a mut Value) -> Result<Value<'a>, Error> {
(self.vtable.as_mut)(val)
}
}
}
static REFLECTED_TYS: SyncOnceCell<RwLock<HashMap<String, Type>>> = SyncOnceCell::new();
pub trait CommonTypeInfo {
fn name(&self) -> String;
fn assoc_fns(&self) -> Vec<AssocFn>;
fn assoc_consts(&self) -> Vec<AssocConst>;
fn as_ref<'a>(&self, val: &'a Value) -> Result<Value<'a>, Error>;
fn as_mut<'a>(&self, val: &'a mut Value) -> Result<Value<'a>, Error>;
}
#[derive(Copy, Clone)]
struct TypeVTable {
name: fn() -> String,
assoc_fns: fn() -> Vec<AssocFn>,
assoc_consts: fn() -> Vec<AssocConst>,
as_ref: for<'a> fn(&'a Value) -> Result<Value<'a>, Error>,
as_mut: for<'a> fn(&'a mut Value) -> Result<Value<'a>, Error>,
}
impl fmt::Debug for TypeVTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"TypeVTable {{ name: {:?}, assoc_fns: {:?}, assoc_consts: {:?}, as_ref: {:p} }}",
self.name, self.assoc_fns, self.assoc_consts, self.as_ref as *const ()
)
}
}
impl TypeVTable {
fn new<T: ?Sized + Reflected>() -> TypeVTable {
TypeVTable {
name: T::name,
assoc_fns: T::assoc_fns,
assoc_consts: T::assoc_consts,
as_ref: <T as Ref>::ref_val,
as_mut: <T as Ref>::mut_val,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum Type {
Primitive(PrimitiveInfo),
Tuple(TupleInfo),
Array(ArrayInfo),
Slice(SliceInfo),
Pointer(PointerInfo),
Reference(ReferenceInfo),
Function(FunctionInfo),
Struct(StructInfo),
TupleStruct(TupleStructInfo),
UnitStruct(UnitStructInfo),
Enum(EnumInfo),
Union(UnionInfo),
}
macro_rules! ty_unwraps {
($($var:ident),+) => {
paste::paste! {
$(
#[doc = "Get this Type as a [`" $var "Info`], panicking on failure."]
#[track_caller]
pub fn [<unwrap_ $var:snake>](&self) -> & [<$var Info>] {
if let Type::$var(info) = self {
info
} else {
panic!(concat!("Attempted to unwrap non-", stringify!($var:lower), " Type as ", stringify!($var:lower)))
}
}
)*
}
}
}
impl Type {
fn add_ty(ty: Type) {
let mut map = REFLECTED_TYS
.get_or_init(|| RwLock::new(HashMap::new()))
.write()
.expect("REFLECTED_TYS not initialized correctly");
let name = ty.name();
if map.contains_key(&name) {
panic!("Type {} already registered", name);
}
map.insert(name, ty);
}
#[doc(hidden)]
pub unsafe fn new_prim<T: ?Sized + Reflected>() {
let ty = Type::Primitive(PrimitiveInfo {
vtable: TypeVTable::new::<T>(),
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_tuple<T: ?Sized + ReflectedTuple>() {
let ty = Type::Tuple(TupleInfo {
vtable: TypeVTable::new::<T>(),
fields: T::fields,
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_array<T: ?Sized + ReflectedArray>() {
let ty = Type::Array(ArrayInfo {
vtable: TypeVTable::new::<T>(),
element: T::element,
length: T::length(),
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_slice<T: ?Sized + ReflectedSlice>() {
let ty = Type::Slice(SliceInfo {
vtable: TypeVTable::new::<T>(),
element: T::element,
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_ptr<T: ReflectedPointer>() {
let ty = Type::Pointer(PointerInfo {
vtable: TypeVTable::new::<T>(),
element: T::element,
mutability: T::mutability(),
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_ref<T: ReflectedReference>() {
let ty = Type::Reference(ReferenceInfo {
vtable: TypeVTable::new::<T>(),
element: T::element,
mutability: T::mutability(),
});
Type::add_ty(ty);
}
#[doc(hidden)]
pub unsafe fn new_fn<T: ReflectedFunction>() {
let ty = Type::Function(FunctionInfo {
vtable: TypeVTable::new::<T>(),
args: T::args,
ret: T::ret,
});
Type::add_ty(ty);
}
pub unsafe fn new_struct<T: ?Sized + ReflectedStruct>() {
let ty = Type::Struct(StructInfo {
vtable: TypeVTable::new::<T>(),
fields: T::fields,
});
Type::add_ty(ty);
}
pub unsafe fn new_tuple_struct<T: ReflectedTupleStruct>() {
let ty = Type::TupleStruct(TupleStructInfo {
vtable: TypeVTable::new::<T>(),
fields: T::fields,
});
Type::add_ty(ty);
}
pub unsafe fn new_unit_struct<T: ReflectedUnitStruct>() {
let ty = Type::UnitStruct(UnitStructInfo {
vtable: TypeVTable::new::<T>(),
});
Type::add_ty(ty);
}
pub unsafe fn new_enum<T: ReflectedEnum>() {
let ty = Type::Enum(EnumInfo {
vtable: TypeVTable::new::<T>(),
variants: T::variants,
});
Type::add_ty(ty);
}
pub unsafe fn new_union<T: ReflectedUnion>() {
let ty = Type::Union(UnionInfo {
vtable: TypeVTable::new::<T>(),
fields: T::fields,
});
Type::add_ty(ty);
}
pub unsafe fn from_name(name: &str) -> Option<Type> {
REFLECTED_TYS
.get_or_init(|| RwLock::new(HashMap::new()))
.read()
.expect("Couldn't get read lock on Reflection mapping")
.get(name)
.copied()
}
pub fn from<T: ?Sized + Reflected>() -> Type {
static INIT: SyncOnceCell<StaticTypeMap<()>> = SyncOnceCell::new();
INIT.get_or_init(StaticTypeMap::new).call_once::<T, _>(|| {
unsafe { T::init() };
});
unsafe { Type::from_name(&T::name()).expect("Type not initialized") }
}
fn as_inner(&self) -> &dyn CommonTypeInfo {
match self {
Type::Primitive(i) => i,
Type::Tuple(i) => i,
Type::Slice(i) => i,
Type::Array(i) => i,
Type::Pointer(i) => i,
Type::Reference(i) => i,
Type::Function(i) => i,
Type::Struct(i) => i,
Type::TupleStruct(i) => i,
Type::UnitStruct(i) => i,
Type::Enum(i) => i,
Type::Union(i) => i,
}
}
ty_unwraps!(
Primitive,
Tuple,
Array,
Slice,
Pointer,
Reference,
Function,
Struct,
TupleStruct,
UnitStruct,
Enum,
Union
);
}
impl PartialEq for Type {
fn eq(&self, other: &Type) -> bool {
self.name() == other.name()
}
}
impl Eq for Type {}
impl Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name().hash(state);
}
}
impl CommonTypeInfo for Type {
fn name(&self) -> String {
self.as_inner().name()
}
fn assoc_fns(&self) -> Vec<AssocFn> {
self.as_inner().assoc_fns()
}
fn assoc_consts(&self) -> Vec<AssocConst> {
self.as_inner().assoc_consts()
}
fn as_ref<'a>(&self, val: &'a Value) -> Result<Value<'a>, Error> {
self.as_inner().as_ref(val)
}
fn as_mut<'a>(&self, val: &'a mut Value) -> Result<Value<'a>, Error> {
self.as_inner().as_mut(val)
}
}
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveInfo {
vtable: TypeVTable,
}
impl_common!(PrimitiveInfo);
#[derive(Debug, Copy, Clone)]
pub struct TupleInfo {
vtable: TypeVTable,
fields: fn() -> Vec<Field>,
}
impl TupleInfo {
pub fn fields(&self) -> Vec<Field> {
(self.fields)()
}
}
impl_common!(TupleInfo);
#[derive(Debug, Copy, Clone)]
pub struct ArrayInfo {
vtable: TypeVTable,
element: fn() -> Type,
length: usize,
}
impl ArrayInfo {
pub fn element(&self) -> Type {
(self.element)()
}
pub fn length(&self) -> usize {
self.length
}
}
impl_common!(ArrayInfo);
#[derive(Debug, Copy, Clone)]
pub struct SliceInfo {
vtable: TypeVTable,
element: fn() -> Type,
}
impl SliceInfo {
pub fn element(&self) -> Type {
(self.element)()
}
}
impl_common!(SliceInfo);
#[derive(Debug, Copy, Clone)]
pub struct PointerInfo {
vtable: TypeVTable,
element: fn() -> Type,
mutability: bool,
}
impl PointerInfo {
pub fn element(&self) -> Type {
(self.element)()
}
pub fn mutability(&self) -> bool {
self.mutability
}
}
impl_common!(PointerInfo);
#[derive(Debug, Copy, Clone)]
pub struct ReferenceInfo {
vtable: TypeVTable,
element: fn() -> Type,
mutability: bool,
}
impl ReferenceInfo {
pub fn element(&self) -> Type {
(self.element)()
}
pub fn mutability(&self) -> bool {
self.mutability
}
}
impl_common!(ReferenceInfo);
#[derive(Debug, Copy, Clone)]
pub struct FunctionInfo {
vtable: TypeVTable,
args: fn() -> Vec<Type>,
ret: fn() -> Type,
}
impl FunctionInfo {
pub fn arg_tys(&self) -> Vec<Type> {
(self.args)()
}
pub fn ret_ty(&self) -> Type {
(self.ret)()
}
}
impl_common!(FunctionInfo);
#[derive(Debug, Copy, Clone)]
pub struct StructInfo {
vtable: TypeVTable,
fields: fn() -> Vec<Field>,
}
impl StructInfo {
pub fn fields(&self) -> Vec<Field> {
(self.fields)()
}
}
impl_common!(StructInfo);
#[derive(Debug, Copy, Clone)]
pub struct TupleStructInfo {
vtable: TypeVTable,
fields: fn() -> Vec<Field>,
}
impl TupleStructInfo {
pub fn fields(&self) -> Vec<Field> {
(self.fields)()
}
}
impl_common!(TupleStructInfo);
#[derive(Debug, Copy, Clone)]
pub struct UnitStructInfo {
vtable: TypeVTable,
}
impl_common!(UnitStructInfo);
#[derive(Debug, Copy, Clone)]
pub struct EnumInfo {
vtable: TypeVTable,
variants: fn() -> Vec<Variant>,
}
impl EnumInfo {
pub fn variants(&self) -> Vec<Variant> {
(self.variants)()
}
pub fn variant_of(&self, val: &Value) -> Result<Variant, Error> {
for i in self.variants() {
if i.is_variant(val)? {
return Ok(i);
}
}
unreachable!("An instance of an enum should always be one of its variants")
}
pub fn is_variant(&self, val: &Value, var: &Variant) -> Result<bool, Error> {
if var.assoc_ty() == Type::Enum(*self) {
var.is_variant(val)
} else {
Err(Error::wrong_type(var.assoc_ty(), Type::Enum(*self)))
}
}
}
impl_common!(EnumInfo);
#[derive(Debug, Copy, Clone)]
pub struct UnionInfo {
vtable: TypeVTable,
fields: fn() -> Vec<UnionField>,
}
impl UnionInfo {
pub fn fields(&self) -> Vec<UnionField> {
(self.fields)()
}
}
impl_common!(UnionInfo);