use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::sync::LazyLock;
use crate::pagable::error::PagableError;
use crate::values::layout::vtable::AValueVTable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DeserTypeId(pub &'static str);
impl pagable::PagableSerialize for DeserTypeId {
fn pagable_serialize(
&self,
serializer: &mut dyn pagable::PagableSerializer,
) -> pagable::Result<()> {
self.0.pagable_serialize(serializer)
}
}
impl<'de> pagable::PagableDeserialize<'de> for DeserTypeId {
fn pagable_deserialize<D: pagable::PagableDeserializer<'de> + ?Sized>(
deserializer: &mut D,
) -> pagable::Result<Self> {
let name = String::pagable_deserialize(deserializer)?;
VTABLE_REGISTRY
.get_key_value(name.as_str())
.map(|(k, _)| *k)
.ok_or_else(|| {
anyhow::anyhow!("Type `{}` was not registered for deserialization", name)
})
}
}
impl DeserTypeId {
#[inline]
pub const fn of<T: ?Sized>() -> Self {
DeserTypeId(std::any::type_name::<T>())
}
#[inline]
pub const fn as_str(&self) -> &'static str {
self.0
}
}
impl Display for DeserTypeId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self.0, f)
}
}
impl Borrow<str> for DeserTypeId {
fn borrow(&self) -> &str {
self.0
}
}
pub struct VTableRegistryEntry {
pub deser_type_id: DeserTypeId,
pub vtable: &'static AValueVTable,
}
inventory::collect!(VTableRegistryEntry);
static VTABLE_REGISTRY: LazyLock<HashMap<DeserTypeId, &'static AValueVTable>> =
LazyLock::new(|| {
inventory::iter::<VTableRegistryEntry>()
.map(|e| (e.deser_type_id, e.vtable))
.collect()
});
#[allow(dead_code)]
pub fn lookup_vtable(deser_type_id: DeserTypeId) -> crate::Result<&'static AValueVTable> {
VTABLE_REGISTRY.get(&deser_type_id).copied().ok_or_else(|| {
PagableError::TypeNotRegistered {
type_id: deser_type_id,
}
.into()
})
}
#[cfg(test)]
pub(crate) fn registered_type_ids() -> Vec<DeserTypeId> {
VTABLE_REGISTRY.keys().copied().collect()
}
#[cfg(test)]
mod tests {
use allocative::Allocative;
use derive_more::Display;
use starlark_derive::Freeze;
use starlark_derive::NoSerialize;
use starlark_derive::StarlarkPagable;
use starlark_derive::Trace;
use starlark_derive::starlark_value;
use super::*;
use crate as starlark;
use crate::starlark_complex_value;
use crate::starlark_simple_value;
use crate::values::Coerce;
use crate::values::ProvidesStaticType;
use crate::values::StarlarkValue;
use crate::values::ValueLifetimeless;
use crate::values::ValueLike;
#[derive(
Debug,
Display,
ProvidesStaticType,
NoSerialize,
Allocative,
StarlarkPagable
)]
#[display("TestSimpleType")]
struct TestSimpleType;
starlark_simple_value!(TestSimpleType);
#[starlark_value(type = "TestSimpleType", skip_pagable)]
impl<'v> StarlarkValue<'v> for TestSimpleType {}
#[derive(
Debug,
Display,
ProvidesStaticType,
NoSerialize,
Allocative,
Clone,
Trace,
Freeze,
Coerce,
StarlarkPagable
)]
#[display("TestComplex")]
#[repr(C)]
struct TestComplexGen<V: ValueLifetimeless> {
_value: V,
}
starlark_complex_value!(TestComplex);
#[starlark_value(type = "TestComplex", skip_pagable)]
impl<'v, V: ValueLike<'v>> StarlarkValue<'v> for TestComplexGen<V> where Self: ProvidesStaticType<'v>
{}
#[test]
fn test_simple_type_is_registered() {
let deser_type_id = DeserTypeId::of::<TestSimpleType>();
let vtable = lookup_vtable(deser_type_id);
assert!(
vtable.is_ok(),
"Expected TestSimpleType to be registered. Available types: {:?}",
registered_type_ids()
);
let vt = vtable.unwrap();
assert_eq!(vt.type_name, "TestSimpleType");
}
#[test]
fn test_complex_type_frozen_is_registered() {
let type_id = DeserTypeId::of::<FrozenTestComplex>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected FrozenTestComplex to be registered. Available types: {:?}",
registered_type_ids()
);
let vt = vtable.unwrap();
assert_eq!(vt.type_name, "TestComplex");
}
#[test]
fn test_lookup_nonexistent_type() {
let result = lookup_vtable(DeserTypeId("this_type_does_not_exist_12345"));
assert!(result.is_err());
match result {
Err(err) => match err.kind() {
crate::ErrorKind::Other(e) => {
let pagable_err = e.downcast_ref::<PagableError>().unwrap();
assert!(
matches!(pagable_err, PagableError::TypeNotRegistered { .. }),
"Expected TypeNotRegistered error"
);
}
_ => panic!("Expected ErrorKind::Other"),
},
Ok(_) => panic!("Expected error, got Ok"),
}
}
#[test]
fn test_starlark_str_is_registered() {
use crate::values::types::string::str_type::StarlarkStr;
let type_id = DeserTypeId::of::<StarlarkStr>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected StarlarkStr to be registered. Available types: {:?}",
registered_type_ids()
);
let vt = vtable.unwrap();
assert!(vt.is_str);
}
#[test]
fn test_frozen_tuple_is_registered() {
use crate::values::types::tuple::value::FrozenTuple;
let type_id = DeserTypeId::of::<FrozenTuple>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected FrozenTuple to be registered. Available types: {:?}",
registered_type_ids()
);
let vt = vtable.unwrap();
assert_eq!(vt.type_name, "tuple");
}
#[test]
fn test_frozen_list_is_registered() {
use crate::values::list::value::ListGen;
use crate::values::types::list::value::FrozenListData;
let type_id = DeserTypeId::of::<ListGen<FrozenListData>>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected ListGen<FrozenListData> to be registered. Available types: {:?}",
registered_type_ids()
);
let vt = vtable.unwrap();
assert_eq!(vt.type_name, "list");
}
#[test]
fn test_type_compiled_non_generic_matcher_is_registered() {
use crate::values::typing::type_compiled::compiled::TypeCompiledImplAsStarlarkValue;
use crate::values::typing::type_compiled::matchers::IsAnyOf;
let type_id = DeserTypeId::of::<TypeCompiledImplAsStarlarkValue<IsAnyOf>>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected TypeCompiledImplAsStarlarkValue<IsAnyOf> to be registered. Available types: {:?}",
registered_type_ids()
);
}
#[test]
fn test_type_compiled_generic_matcher_is_registered() {
use crate::values::typing::type_compiled::compiled::TypeCompiledImplAsStarlarkValue;
use crate::values::typing::type_compiled::matcher::TypeMatcherBox;
use crate::values::typing::type_compiled::matchers::IsListOf;
let type_id =
DeserTypeId::of::<TypeCompiledImplAsStarlarkValue<IsListOf<TypeMatcherBox>>>();
let vtable = lookup_vtable(type_id);
assert!(
vtable.is_ok(),
"Expected TypeCompiledImplAsStarlarkValue<IsListOf<TypeMatcherBox>> to be registered. Available types: {:?}",
registered_type_ids()
);
}
}