#![allow(clippy::new_without_default)]
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
};
use rbx_types::{Variant, VariantType};
use serde::{Deserialize, Serialize};
use crate::{ClassTag, PropertyMigration, PropertyTag};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[non_exhaustive]
pub struct ReflectionDatabase<'a> {
pub version: [u32; 4],
#[serde(serialize_with = "crate::serde_util::ordered_map")]
pub classes: HashMap<Cow<'a, str>, ClassDescriptor<'a>>,
#[serde(default, serialize_with = "crate::serde_util::ordered_map")]
pub enums: HashMap<Cow<'a, str>, EnumDescriptor<'a>>,
}
impl<'a> ReflectionDatabase<'a> {
pub fn new() -> Self {
Self {
version: [0, 0, 0, 0],
classes: HashMap::new(),
enums: HashMap::new(),
}
}
pub fn superclasses(
&'a self,
descriptor: &'a ClassDescriptor<'a>,
) -> Option<Vec<&'a ClassDescriptor<'a>>> {
let mut list = Vec::with_capacity(6);
let mut current_class = Some(descriptor);
while let Some(class) = current_class {
list.push(class);
current_class = class.superclass.as_ref().and_then(|s| self.classes.get(s));
}
Some(list)
}
pub fn superclasses_iter(
&'a self,
descriptor: &'a ClassDescriptor<'a>,
) -> impl Iterator<Item = &'a ClassDescriptor<'a>> {
std::iter::successors(Some(descriptor), move |class| {
class.superclass.as_ref().and_then(|s| self.classes.get(s))
})
}
pub fn has_superclass(
&self,
descriptor: &ClassDescriptor,
superclass_descriptor: &ClassDescriptor,
) -> bool {
self.superclasses_iter(descriptor)
.any(|class_descriptor| class_descriptor.name == superclass_descriptor.name)
}
pub fn find_default_property(
&'a self,
mut class: &'a ClassDescriptor<'a>,
property_name: &str,
) -> Option<&'a Variant> {
loop {
match class.default_properties.get(property_name) {
None => {
class = self
.classes
.get(class.superclass.as_ref()?)
.expect("superclass that is Some should exist in reflection database")
}
default_value => return default_value,
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[non_exhaustive]
pub struct ClassDescriptor<'a> {
pub name: Cow<'a, str>,
#[serde(serialize_with = "crate::serde_util::ordered_set")]
pub tags: HashSet<ClassTag>,
#[serde(default)]
pub superclass: Option<Cow<'a, str>>,
#[serde(serialize_with = "crate::serde_util::ordered_map")]
pub properties: HashMap<Cow<'a, str>, PropertyDescriptor<'a>>,
#[serde(serialize_with = "crate::serde_util::ordered_map")]
pub default_properties: HashMap<Cow<'a, str>, Variant>,
}
impl<'a> ClassDescriptor<'a> {
pub fn new<S: Into<Cow<'a, str>>>(name: S) -> Self {
Self {
name: name.into(),
tags: HashSet::new(),
superclass: None,
properties: HashMap::new(),
default_properties: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[non_exhaustive]
pub struct PropertyDescriptor<'a> {
pub name: Cow<'a, str>,
pub scriptability: Scriptability,
pub data_type: DataType<'a>,
#[serde(serialize_with = "crate::serde_util::ordered_set")]
pub tags: HashSet<PropertyTag>,
pub kind: PropertyKind<'a>,
}
impl<'a> PropertyDescriptor<'a> {
pub fn new<S: Into<Cow<'a, str>>>(name: S, data_type: DataType<'a>) -> Self {
Self {
name: name.into(),
scriptability: Scriptability::None,
data_type,
tags: HashSet::new(),
kind: PropertyKind::Canonical {
serialization: PropertySerialization::Serializes,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PropertyKind<'a> {
#[serde(rename_all = "PascalCase")]
Canonical {
serialization: PropertySerialization<'a>,
},
#[serde(rename_all = "PascalCase")]
Alias { alias_for: Cow<'a, str> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PropertySerialization<'a> {
Serializes,
DoesNotSerialize,
SerializesAs(Cow<'a, str>),
Migrate(PropertyMigration),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum DataType<'a> {
Value(VariantType),
Enum(Cow<'a, str>),
}
impl DataType<'_> {
pub fn ty(&self) -> VariantType {
match self {
DataType::Value(variant_type) => *variant_type,
DataType::Enum(_) => VariantType::Enum,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Scriptability {
None,
ReadWrite,
Read,
Write,
Custom,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EnumDescriptor<'a> {
pub name: Cow<'a, str>,
#[serde(serialize_with = "crate::serde_util::ordered_map")]
pub items: HashMap<Cow<'a, str>, u32>,
}
impl<'a> EnumDescriptor<'a> {
pub fn new<S: Into<Cow<'a, str>>>(name: S) -> Self {
Self {
name: name.into(),
items: HashMap::new(),
}
}
}