2023-03-19 12:23:25 +00:00
|
|
|
use std::borrow::{Borrow, Cow};
|
|
|
|
|
2023-03-19 14:48:39 +00:00
|
|
|
use rbx_dom_weak::types::{Variant as DomValue, VariantType as DomType};
|
2023-03-19 12:23:25 +00:00
|
|
|
use rbx_reflection::{ClassTag, DataType};
|
|
|
|
|
2023-03-19 14:48:39 +00:00
|
|
|
pub(crate) struct PropertyInfo {
|
|
|
|
pub enum_name: Option<Cow<'static, str>>,
|
|
|
|
pub enum_default: Option<u32>,
|
|
|
|
pub value_type: Option<DomType>,
|
|
|
|
pub value_default: Option<&'static DomValue>,
|
2023-03-19 12:23:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-03-19 14:48:39 +00:00
|
|
|
Finds the info of a property of the given class.
|
2023-03-19 12:23:25 +00:00
|
|
|
|
2023-03-19 14:48:39 +00:00
|
|
|
Returns `None` if the class or property does not exist.
|
2023-03-19 12:23:25 +00:00
|
|
|
*/
|
2023-03-19 14:48:39 +00:00
|
|
|
pub(crate) fn find_property_info(
|
2023-03-19 12:23:25 +00:00
|
|
|
instance_class: impl AsRef<str>,
|
|
|
|
property_name: impl AsRef<str>,
|
2023-03-19 14:48:39 +00:00
|
|
|
) -> Option<PropertyInfo> {
|
2023-03-19 12:23:25 +00:00
|
|
|
let db = rbx_reflection_database::get();
|
|
|
|
let class = db.classes.get(instance_class.as_ref())?;
|
|
|
|
|
2023-03-19 14:48:39 +00:00
|
|
|
let property_name = property_name.as_ref();
|
|
|
|
let prop_definition = class.properties.get(property_name)?;
|
|
|
|
let prop_default = class.default_properties.get(property_name);
|
2023-03-19 12:23:25 +00:00
|
|
|
|
2023-03-19 14:48:39 +00:00
|
|
|
match &prop_definition.data_type {
|
|
|
|
DataType::Enum(enum_name) => Some(PropertyInfo {
|
|
|
|
enum_name: Some(Cow::Borrowed(enum_name)),
|
|
|
|
enum_default: prop_default.and_then(|default| match default {
|
|
|
|
DomValue::Enum(enum_default) => Some(enum_default.to_u32()),
|
|
|
|
_ => None,
|
|
|
|
}),
|
|
|
|
value_type: None,
|
|
|
|
value_default: None,
|
|
|
|
}),
|
|
|
|
DataType::Value(value_type) => Some(PropertyInfo {
|
|
|
|
enum_name: None,
|
|
|
|
enum_default: None,
|
|
|
|
value_type: Some(*value_type),
|
|
|
|
value_default: prop_default,
|
|
|
|
}),
|
|
|
|
_ => Some(PropertyInfo {
|
|
|
|
enum_name: None,
|
|
|
|
enum_default: None,
|
|
|
|
value_type: None,
|
|
|
|
value_default: None,
|
|
|
|
}),
|
2023-03-19 12:23:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Checks if an instance class exists in the reflection database.
|
|
|
|
*/
|
|
|
|
pub fn class_exists(class_name: impl AsRef<str>) -> bool {
|
|
|
|
let db = rbx_reflection_database::get();
|
|
|
|
db.classes.contains_key(class_name.as_ref())
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Checks if an instance class matches a given class or superclass, similar to
|
|
|
|
[Instance::IsA](https://create.roblox.com/docs/reference/engine/classes/Instance#IsA)
|
|
|
|
from the Roblox standard library.
|
|
|
|
|
|
|
|
Note that this function may return `None` if it encounters a class or superclass
|
|
|
|
that does not exist in the currently known class reflection database.
|
|
|
|
*/
|
|
|
|
pub fn class_is_a(instance_class: impl AsRef<str>, class_name: impl AsRef<str>) -> Option<bool> {
|
|
|
|
let mut instance_class = instance_class.as_ref();
|
|
|
|
let class_name = class_name.as_ref();
|
|
|
|
|
|
|
|
if class_name == "Instance" || instance_class == class_name {
|
|
|
|
Some(true)
|
|
|
|
} else {
|
|
|
|
let db = rbx_reflection_database::get();
|
|
|
|
|
|
|
|
while instance_class != class_name {
|
|
|
|
let class_descriptor = db.classes.get(instance_class)?;
|
|
|
|
if let Some(sup) = &class_descriptor.superclass {
|
|
|
|
instance_class = sup.borrow();
|
|
|
|
} else {
|
|
|
|
return Some(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Checks if an instance class is a service.
|
|
|
|
|
|
|
|
This is separate from [`class_is_a`] since services do not share a
|
|
|
|
common base class, and are instead determined through reflection tags.
|
|
|
|
|
|
|
|
Note that this function may return `None` if it encounters a class or superclass
|
|
|
|
that does not exist in the currently known class reflection database.
|
|
|
|
*/
|
|
|
|
pub fn class_is_a_service(instance_class: impl AsRef<str>) -> Option<bool> {
|
|
|
|
let mut instance_class = instance_class.as_ref();
|
|
|
|
|
|
|
|
let db = rbx_reflection_database::get();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let class_descriptor = db.classes.get(instance_class)?;
|
|
|
|
if class_descriptor.tags.contains(&ClassTag::Service) {
|
|
|
|
return Some(true);
|
|
|
|
} else if let Some(sup) = &class_descriptor.superclass {
|
|
|
|
instance_class = sup.borrow();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn is_a_class_valid() {
|
|
|
|
assert_eq!(class_is_a("Part", "Part"), Some(true));
|
|
|
|
assert_eq!(class_is_a("Part", "BasePart"), Some(true));
|
|
|
|
assert_eq!(class_is_a("Part", "PVInstance"), Some(true));
|
|
|
|
assert_eq!(class_is_a("Part", "Instance"), Some(true));
|
|
|
|
|
|
|
|
assert_eq!(class_is_a("Workspace", "Workspace"), Some(true));
|
|
|
|
assert_eq!(class_is_a("Workspace", "Model"), Some(true));
|
|
|
|
assert_eq!(class_is_a("Workspace", "Instance"), Some(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn is_a_class_invalid() {
|
|
|
|
assert_eq!(class_is_a("Part", "part"), Some(false));
|
|
|
|
assert_eq!(class_is_a("Part", "Base-Part"), Some(false));
|
|
|
|
assert_eq!(class_is_a("Part", "Model"), Some(false));
|
|
|
|
assert_eq!(class_is_a("Part", "Paart"), Some(false));
|
|
|
|
|
|
|
|
assert_eq!(class_is_a("Workspace", "Service"), Some(false));
|
|
|
|
assert_eq!(class_is_a("Workspace", "."), Some(false));
|
|
|
|
assert_eq!(class_is_a("Workspace", ""), Some(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn is_a_service_valid() {
|
|
|
|
assert_eq!(class_is_a_service("Workspace"), Some(true));
|
|
|
|
assert_eq!(class_is_a_service("PhysicsService"), Some(true));
|
|
|
|
assert_eq!(class_is_a_service("ReplicatedFirst"), Some(true));
|
|
|
|
assert_eq!(class_is_a_service("CSGDictionaryService"), Some(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn is_a_service_invalid() {
|
|
|
|
assert_eq!(class_is_a_service("Camera"), Some(false));
|
|
|
|
assert_eq!(class_is_a_service("Terrain"), Some(false));
|
|
|
|
assert_eq!(class_is_a_service("Work-space"), None);
|
|
|
|
assert_eq!(class_is_a_service("CSG Dictionary Service"), None);
|
|
|
|
}
|
|
|
|
}
|