use crate::prelude::{
any::TypeId,
collections::BTreeMap,
fmt::Debug,
num::NonZeroU32,
vec::Vec,
};
use crate::{
form::{
Form,
FormString,
PortableForm,
},
interner::{
Interner,
UntrackedSymbol,
},
meta_type::MetaType,
Type,
};
use scale::{
Decode,
Encode,
};
#[cfg(feature = "serde")]
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
pub trait IntoPortable {
type Output;
fn into_portable(self, registry: &mut Registry) -> Self::Output;
}
impl IntoPortable for &'static str {
type Output = <PortableForm as Form>::String;
fn into_portable(self, _registry: &mut Registry) -> Self::Output {
self
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Registry {
type_table: Interner<TypeId>,
types: BTreeMap<UntrackedSymbol<core::any::TypeId>, Type<PortableForm>>,
}
impl Default for Registry {
fn default() -> Self {
Self::new()
}
}
impl Registry {
pub fn new() -> Self {
Self {
type_table: Interner::new(),
types: BTreeMap::new(),
}
}
fn intern_type_id(&mut self, type_id: TypeId) -> (bool, UntrackedSymbol<TypeId>) {
let (inserted, symbol) = self.type_table.intern_or_get(type_id);
(inserted, symbol.into_untracked())
}
pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol<TypeId> {
let (inserted, symbol) = self.intern_type_id(ty.type_id());
if inserted {
let portable_id = ty.type_info().into_portable(self);
self.types.insert(symbol, portable_id);
}
symbol
}
pub fn register_types<I>(&mut self, iter: I) -> Vec<UntrackedSymbol<TypeId>>
where
I: IntoIterator<Item = MetaType>,
{
iter.into_iter()
.map(|i| self.register_type(&i))
.collect::<Vec<_>>()
}
pub fn map_into_portable<I, T>(&mut self, iter: I) -> Vec<T::Output>
where
I: IntoIterator<Item = T>,
T: IntoPortable,
{
iter.into_iter()
.map(|i| i.into_portable(self))
.collect::<Vec<_>>()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned"))
)]
pub struct PortableRegistry<S = &'static str>
where
S: FormString,
{
types: Vec<Type<PortableForm<S>>>,
}
impl From<Registry> for PortableRegistry {
fn from(registry: Registry) -> Self {
PortableRegistry {
types: registry.types.values().cloned().collect::<Vec<_>>(),
}
}
}
impl<S> PortableRegistry<S>
where
S: FormString,
{
pub fn resolve(&self, id: NonZeroU32) -> Option<&Type<PortableForm<S>>> {
self.types.get((id.get() - 1) as usize)
}
pub fn enumerate(
&self,
) -> impl Iterator<Item = (NonZeroU32, &Type<PortableForm<S>>)> {
self.types.iter().enumerate().map(|(i, ty)| {
let id = NonZeroU32::new(i as u32 + 1).expect("i + 1 > 0; qed");
(id, ty)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
build::Fields,
meta_type,
Path,
TypeDef,
TypeInfo,
};
#[test]
fn readonly_enumerate() {
let mut registry = Registry::new();
registry.register_type(&MetaType::new::<u32>());
registry.register_type(&MetaType::new::<bool>());
registry.register_type(&MetaType::new::<Option<(u32, bool)>>());
let readonly: PortableRegistry = registry.into();
assert_eq!(4, readonly.enumerate().count());
let mut expected = 1;
for (i, _) in readonly.enumerate() {
assert_eq!(NonZeroU32::new(expected).unwrap(), i);
expected += 1;
}
}
#[test]
fn recursive_struct_with_references() {
#[allow(unused)]
struct RecursiveRefs<'a> {
boxed: Box<RecursiveRefs<'a>>,
reference: &'a RecursiveRefs<'a>,
mutable_reference: &'a mut RecursiveRefs<'a>,
}
impl TypeInfo for RecursiveRefs<'static> {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("RecursiveRefs", module_path!()))
.composite(
Fields::named()
.field_of::<Box<RecursiveRefs>>(
"boxed",
"Box < RecursiveRefs >",
)
.field_of::<&'static RecursiveRefs<'static>>(
"reference",
"&RecursiveRefs",
)
.field_of::<&'static mut RecursiveRefs<'static>>(
"mutable_reference",
"&mut RecursiveRefs",
),
)
.into()
}
}
let mut registry = Registry::new();
let type_id = registry.register_type(&meta_type::<RecursiveRefs>());
let recursive = registry.types.get(&type_id).unwrap();
if let TypeDef::Composite(composite) = recursive.type_def() {
for field in composite.fields() {
assert_eq!(*field.ty(), type_id)
}
} else {
panic!("Should be a composite type definition")
}
}
}