mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
- Made `format_f32` and `format_f64` functions public to allow external access. - Introduced new constants for disk options and buffer sizes for `f32` and `f64`. - Added `cleanup_f32` function to clean up float values before comparison. - Implemented `disk_eq_f32` and `disk_eq_f64` functions to compare float values based on their disk representation. - Updated `variant_eq_disk` to utilize the new disk equality functions, improving accuracy in matching algorithms. These changes aim to enhance the precision of float comparisons and ensure consistent behavior across different representations in the system.
636 lines
25 KiB
Rust
636 lines
25 KiB
Rust
use std::borrow::Borrow;
|
|
|
|
use anyhow::{bail, format_err};
|
|
use rbx_dom_weak::types::{
|
|
Attributes, CFrame, Color3, ColorSequence, ColorSequenceKeypoint, Content, ContentId,
|
|
ContentType, CustomPhysicalProperties, Enum, Font, MaterialColors, Matrix3, NumberSequence,
|
|
NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, Region3, Tags, UDim, UDim2, Variant,
|
|
VariantType, Vector2, Vector3,
|
|
};
|
|
use rbx_reflection::{DataType, PropertyDescriptor};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::REF_PATH_ATTRIBUTE_PREFIX;
|
|
|
|
/// A user-friendly version of `Variant` that supports specifying ambiguous
|
|
/// values. Ambiguous values need a reflection database to be resolved to a
|
|
/// usable value.
|
|
///
|
|
/// This type is used in Rojo projects and JSON models to make specifying the
|
|
/// most common types of properties, like strings or vectors, much easier.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum UnresolvedValue {
|
|
FullyQualified(Variant),
|
|
Ambiguous(AmbiguousValue),
|
|
}
|
|
|
|
impl UnresolvedValue {
|
|
pub fn resolve(self, class_name: &str, prop_name: &str) -> anyhow::Result<Variant> {
|
|
match self {
|
|
UnresolvedValue::FullyQualified(full) => Ok(full),
|
|
UnresolvedValue::Ambiguous(partial) => partial.resolve(class_name, prop_name),
|
|
}
|
|
}
|
|
|
|
pub fn resolve_unambiguous(self) -> anyhow::Result<Variant> {
|
|
match self {
|
|
UnresolvedValue::FullyQualified(full) => Ok(full),
|
|
UnresolvedValue::Ambiguous(partial) => partial.resolve_unambiguous(),
|
|
}
|
|
}
|
|
|
|
/// Creates an `UnresolvedValue` from a variant, using a class and property
|
|
/// name to potentially allow for ambiguous Enum variants.
|
|
pub fn from_variant(variant: Variant, class_name: &str, prop_name: &str) -> Self {
|
|
let descriptor = find_descriptor(class_name, prop_name);
|
|
if descriptor.is_some() {
|
|
// We can only use an ambiguous syntax if the property is known
|
|
// to the reflection database.
|
|
Self::Ambiguous(match variant {
|
|
Variant::Enum(rbx_enum) => {
|
|
if let Some(property) = descriptor {
|
|
if let DataType::Enum(enum_name) = &property.data_type {
|
|
let database = rbx_reflection_database::get().unwrap();
|
|
if let Some(enum_descriptor) = database.enums.get(enum_name) {
|
|
for (variant_name, id) in &enum_descriptor.items {
|
|
if *id == rbx_enum.to_u32() {
|
|
return Self::Ambiguous(AmbiguousValue::String(
|
|
variant_name.to_string(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Self::FullyQualified(variant);
|
|
}
|
|
Variant::Bool(bool) => AmbiguousValue::Bool(bool),
|
|
Variant::Float32(n) => AmbiguousValue::Number32(n),
|
|
Variant::Float64(n) => AmbiguousValue::Number(n),
|
|
Variant::Int32(n) => AmbiguousValue::Number(n as f64),
|
|
Variant::Int64(n) => AmbiguousValue::Number(n as f64),
|
|
Variant::String(str) => AmbiguousValue::String(str),
|
|
Variant::Tags(tags) => {
|
|
AmbiguousValue::StringArray(tags.iter().map(|s| s.to_string()).collect())
|
|
}
|
|
Variant::Content(ref content) => match content.value() {
|
|
ContentType::None => AmbiguousValue::String(String::new()),
|
|
ContentType::Uri(uri) => AmbiguousValue::String(uri.clone()),
|
|
_ => return Self::FullyQualified(variant),
|
|
},
|
|
Variant::ContentId(content) => AmbiguousValue::String(content.into_string()),
|
|
Variant::Vector2(vector) => {
|
|
AmbiguousValue::Array2([cleanup_f32(vector.x), cleanup_f32(vector.y)])
|
|
}
|
|
Variant::Vector3(vector) => AmbiguousValue::Array3([
|
|
cleanup_f32(vector.x),
|
|
cleanup_f32(vector.y),
|
|
cleanup_f32(vector.z),
|
|
]),
|
|
Variant::Color3(color) => AmbiguousValue::Array3([
|
|
cleanup_f32(color.r),
|
|
cleanup_f32(color.g),
|
|
cleanup_f32(color.b),
|
|
]),
|
|
Variant::CFrame(cf) => AmbiguousValue::Array12([
|
|
cleanup_f32(cf.position.x),
|
|
cleanup_f32(cf.position.y),
|
|
cleanup_f32(cf.position.z),
|
|
cleanup_f32(cf.orientation.x.x),
|
|
cleanup_f32(cf.orientation.x.y),
|
|
cleanup_f32(cf.orientation.x.z),
|
|
cleanup_f32(cf.orientation.y.x),
|
|
cleanup_f32(cf.orientation.y.y),
|
|
cleanup_f32(cf.orientation.y.z),
|
|
cleanup_f32(cf.orientation.z.x),
|
|
cleanup_f32(cf.orientation.z.y),
|
|
cleanup_f32(cf.orientation.z.z),
|
|
]),
|
|
Variant::UDim(udim) => {
|
|
return Self::FullyQualified(Variant::UDim(UDim::new(
|
|
cleanup_f32(udim.scale),
|
|
udim.offset,
|
|
)));
|
|
}
|
|
Variant::UDim2(udim2) => {
|
|
return Self::FullyQualified(Variant::UDim2(UDim2::new(
|
|
UDim::new(cleanup_f32(udim2.x.scale), udim2.x.offset),
|
|
UDim::new(cleanup_f32(udim2.y.scale), udim2.y.offset),
|
|
)));
|
|
}
|
|
Variant::OptionalCFrame(Some(cf)) => {
|
|
return Self::FullyQualified(Variant::OptionalCFrame(Some(CFrame::new(
|
|
Vector3::new(
|
|
cleanup_f32(cf.position.x),
|
|
cleanup_f32(cf.position.y),
|
|
cleanup_f32(cf.position.z),
|
|
),
|
|
Matrix3::new(
|
|
Vector3::new(
|
|
cleanup_f32(cf.orientation.x.x),
|
|
cleanup_f32(cf.orientation.x.y),
|
|
cleanup_f32(cf.orientation.x.z),
|
|
),
|
|
Vector3::new(
|
|
cleanup_f32(cf.orientation.y.x),
|
|
cleanup_f32(cf.orientation.y.y),
|
|
cleanup_f32(cf.orientation.y.z),
|
|
),
|
|
Vector3::new(
|
|
cleanup_f32(cf.orientation.z.x),
|
|
cleanup_f32(cf.orientation.z.y),
|
|
cleanup_f32(cf.orientation.z.z),
|
|
),
|
|
),
|
|
))));
|
|
}
|
|
Variant::Rect(rect) => {
|
|
return Self::FullyQualified(Variant::Rect(Rect::new(
|
|
Vector2::new(cleanup_f32(rect.min.x), cleanup_f32(rect.min.y)),
|
|
Vector2::new(cleanup_f32(rect.max.x), cleanup_f32(rect.max.y)),
|
|
)));
|
|
}
|
|
Variant::Ray(ray) => {
|
|
return Self::FullyQualified(Variant::Ray(Ray::new(
|
|
Vector3::new(
|
|
cleanup_f32(ray.origin.x),
|
|
cleanup_f32(ray.origin.y),
|
|
cleanup_f32(ray.origin.z),
|
|
),
|
|
Vector3::new(
|
|
cleanup_f32(ray.direction.x),
|
|
cleanup_f32(ray.direction.y),
|
|
cleanup_f32(ray.direction.z),
|
|
),
|
|
)));
|
|
}
|
|
Variant::Region3(region) => {
|
|
return Self::FullyQualified(Variant::Region3(Region3::new(
|
|
Vector3::new(
|
|
cleanup_f32(region.min.x),
|
|
cleanup_f32(region.min.y),
|
|
cleanup_f32(region.min.z),
|
|
),
|
|
Vector3::new(
|
|
cleanup_f32(region.max.x),
|
|
cleanup_f32(region.max.y),
|
|
cleanup_f32(region.max.z),
|
|
),
|
|
)));
|
|
}
|
|
Variant::NumberSequence(seq) => {
|
|
return Self::FullyQualified(Variant::NumberSequence(NumberSequence {
|
|
keypoints: seq
|
|
.keypoints
|
|
.into_iter()
|
|
.map(|kp| {
|
|
NumberSequenceKeypoint::new(
|
|
cleanup_f32(kp.time),
|
|
cleanup_f32(kp.value),
|
|
cleanup_f32(kp.envelope),
|
|
)
|
|
})
|
|
.collect(),
|
|
}));
|
|
}
|
|
Variant::ColorSequence(seq) => {
|
|
return Self::FullyQualified(Variant::ColorSequence(ColorSequence {
|
|
keypoints: seq
|
|
.keypoints
|
|
.into_iter()
|
|
.map(|kp| {
|
|
ColorSequenceKeypoint::new(
|
|
cleanup_f32(kp.time),
|
|
Color3::new(
|
|
cleanup_f32(kp.color.r),
|
|
cleanup_f32(kp.color.g),
|
|
cleanup_f32(kp.color.b),
|
|
),
|
|
)
|
|
})
|
|
.collect(),
|
|
}));
|
|
}
|
|
Variant::PhysicalProperties(PhysicalProperties::Custom(cpp)) => {
|
|
return Self::FullyQualified(Variant::PhysicalProperties(
|
|
PhysicalProperties::Custom(CustomPhysicalProperties::new(
|
|
cleanup_f32(cpp.density()),
|
|
cleanup_f32(cpp.friction()),
|
|
cleanup_f32(cpp.elasticity()),
|
|
cleanup_f32(cpp.friction_weight()),
|
|
cleanup_f32(cpp.elasticity_weight()),
|
|
cleanup_f32(cpp.acoustic_absorption()),
|
|
)),
|
|
));
|
|
}
|
|
Variant::Attributes(attr) => AmbiguousValue::Attributes(attr),
|
|
Variant::Font(font) => AmbiguousValue::Font(font),
|
|
Variant::MaterialColors(colors) => AmbiguousValue::MaterialColors(colors),
|
|
_ => {
|
|
return Self::FullyQualified(variant);
|
|
}
|
|
})
|
|
} else {
|
|
Self::FullyQualified(variant)
|
|
}
|
|
}
|
|
|
|
/// Creates an `UnresolvedValue` from a variant, only returning ambiguous
|
|
/// values if they're able to be resolved in a context-free environment.
|
|
pub fn from_variant_unambiguous(variant: Variant) -> Self {
|
|
match variant {
|
|
Variant::String(str) => Self::Ambiguous(AmbiguousValue::String(str)),
|
|
Variant::Float64(number) => Self::Ambiguous(AmbiguousValue::Number(number)),
|
|
Variant::Bool(bool) => Self::Ambiguous(AmbiguousValue::Bool(bool)),
|
|
Variant::BinaryString(bstr) => match std::str::from_utf8(bstr.as_ref()) {
|
|
Ok(_) => Self::Ambiguous(AmbiguousValue::String(
|
|
String::from_utf8(bstr.into_vec()).unwrap(),
|
|
)),
|
|
Err(_) => Self::FullyQualified(Variant::BinaryString(bstr)),
|
|
},
|
|
_ => Self::FullyQualified(variant),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum AmbiguousValue {
|
|
Bool(bool),
|
|
String(String),
|
|
StringArray(Vec<String>),
|
|
Number(f64),
|
|
Number32(f32),
|
|
Array2([f32; 2]),
|
|
Array3([f32; 3]),
|
|
Array4([f32; 4]),
|
|
Array12([f32; 12]),
|
|
Attributes(Attributes),
|
|
Font(Font),
|
|
MaterialColors(MaterialColors),
|
|
}
|
|
|
|
impl AmbiguousValue {
|
|
pub fn resolve(self, class_name: &str, prop_name: &str) -> anyhow::Result<Variant> {
|
|
let property = find_descriptor(class_name, prop_name)
|
|
.ok_or_else(|| format_err!("Unknown property {}.{}", class_name, prop_name))?;
|
|
|
|
match &property.data_type {
|
|
DataType::Enum(enum_name) => {
|
|
let database = rbx_reflection_database::get().unwrap();
|
|
|
|
let enum_descriptor = database.enums.get(enum_name).ok_or_else(|| {
|
|
format_err!("Unknown enum {}. This is a Rojo bug!", enum_name)
|
|
})?;
|
|
|
|
let error = |what: &str| {
|
|
let mut all_values = enum_descriptor
|
|
.items
|
|
.keys()
|
|
.map(|value| value.borrow())
|
|
.collect::<Vec<_>>();
|
|
all_values.sort();
|
|
|
|
let examples = nonexhaustive_list(&all_values);
|
|
|
|
format_err!(
|
|
"Invalid value for property {}.{}. Got {} but \
|
|
expected a member of the {} enum such as {}",
|
|
class_name,
|
|
prop_name,
|
|
what,
|
|
enum_name,
|
|
examples,
|
|
)
|
|
};
|
|
|
|
let value = match self {
|
|
AmbiguousValue::String(value) => value,
|
|
unresolved => return Err(error(unresolved.describe())),
|
|
};
|
|
|
|
let resolved = enum_descriptor
|
|
.items
|
|
.get(value.as_str())
|
|
.ok_or_else(|| error(value.as_str()))?;
|
|
|
|
Ok(Enum::from_u32(*resolved).into())
|
|
}
|
|
DataType::Value(variant_ty) => match (variant_ty, self) {
|
|
(VariantType::Bool, AmbiguousValue::Bool(value)) => Ok(value.into()),
|
|
|
|
(VariantType::Float32, AmbiguousValue::Number32(value)) => Ok(value.into()),
|
|
(VariantType::Float32, AmbiguousValue::Number(value)) => Ok((value as f32).into()),
|
|
(VariantType::Float64, AmbiguousValue::Number(value)) => Ok(value.into()),
|
|
(VariantType::Int32, AmbiguousValue::Number(value)) => Ok((value as i32).into()),
|
|
(VariantType::Int64, AmbiguousValue::Number(value)) => Ok((value as i64).into()),
|
|
|
|
(VariantType::String, AmbiguousValue::String(value)) => Ok(value.into()),
|
|
(VariantType::Tags, AmbiguousValue::StringArray(value)) => {
|
|
Ok(value.iter().map(String::as_str).collect::<Tags>().into())
|
|
}
|
|
(VariantType::Content, AmbiguousValue::String(value)) => {
|
|
Ok(Content::from(value).into())
|
|
}
|
|
(VariantType::ContentId, AmbiguousValue::String(value)) => {
|
|
Ok(ContentId::from(value).into())
|
|
}
|
|
|
|
(VariantType::Vector2, AmbiguousValue::Array2(value)) => {
|
|
Ok(Vector2::new(value[0], value[1]).into())
|
|
}
|
|
|
|
(VariantType::Vector3, AmbiguousValue::Array3(value)) => {
|
|
Ok(Vector3::new(value[0], value[1], value[2]).into())
|
|
}
|
|
|
|
(VariantType::Color3, AmbiguousValue::Array3(value)) => {
|
|
Ok(Color3::new(value[0], value[1], value[2]).into())
|
|
}
|
|
|
|
(VariantType::CFrame, AmbiguousValue::Array12(value)) => {
|
|
let value = value.map(|v| v);
|
|
let pos = Vector3::new(value[0], value[1], value[2]);
|
|
let orientation = Matrix3::new(
|
|
Vector3::new(value[3], value[4], value[5]),
|
|
Vector3::new(value[6], value[7], value[8]),
|
|
Vector3::new(value[9], value[10], value[11]),
|
|
);
|
|
|
|
Ok(CFrame::new(pos, orientation).into())
|
|
}
|
|
|
|
(VariantType::Attributes, AmbiguousValue::Attributes(value)) => Ok(value.into()),
|
|
|
|
(VariantType::Font, AmbiguousValue::Font(value)) => Ok(value.into()),
|
|
|
|
(VariantType::MaterialColors, AmbiguousValue::MaterialColors(value)) => {
|
|
Ok(value.into())
|
|
}
|
|
|
|
(VariantType::Ref, AmbiguousValue::String(_)) => Err(format_err!(
|
|
"Cannot resolve Ref properties as a String. \
|
|
Use an attribute named `{REF_PATH_ATTRIBUTE_PREFIX}{prop_name}` with the path to the target instance"
|
|
)),
|
|
(_, unresolved) => Err(format_err!(
|
|
"Wrong type of value for property {}.{}. Expected {:?}, got {}",
|
|
class_name,
|
|
prop_name,
|
|
variant_ty,
|
|
unresolved.describe(),
|
|
)),
|
|
},
|
|
_ => Err(format_err!(
|
|
"Unknown data type for property {}.{}",
|
|
class_name,
|
|
prop_name
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn resolve_unambiguous(self) -> anyhow::Result<Variant> {
|
|
match self {
|
|
AmbiguousValue::Bool(value) => Ok(value.into()),
|
|
AmbiguousValue::Number(value) => Ok(value.into()),
|
|
AmbiguousValue::String(value) => Ok(value.into()),
|
|
|
|
other => bail!("Cannot unambiguously resolve the value {other:?}"),
|
|
}
|
|
}
|
|
|
|
fn describe(&self) -> &'static str {
|
|
match self {
|
|
AmbiguousValue::Bool(_) => "a bool",
|
|
AmbiguousValue::String(_) => "a string",
|
|
AmbiguousValue::StringArray(_) => "an array of strings",
|
|
AmbiguousValue::Number(_) => "a number",
|
|
AmbiguousValue::Number32(_) => "a number",
|
|
AmbiguousValue::Array2(_) => "an array of two numbers",
|
|
AmbiguousValue::Array3(_) => "an array of three numbers",
|
|
AmbiguousValue::Array4(_) => "an array of four numbers",
|
|
AmbiguousValue::Array12(_) => "an array of twelve numbers",
|
|
AmbiguousValue::Attributes(_) => "an object containing attributes",
|
|
AmbiguousValue::Font(_) => "an object describing a Font",
|
|
AmbiguousValue::MaterialColors(_) => "an object describing MaterialColors",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn cleanup_f32(v: f32) -> f32 {
|
|
let cleaned = format!("{:.6}", v).parse::<f32>().unwrap_or(v);
|
|
if cleaned == 0.0 {
|
|
0.0
|
|
} else {
|
|
cleaned
|
|
}
|
|
}
|
|
|
|
fn find_descriptor(
|
|
class_name: &str,
|
|
prop_name: &str,
|
|
) -> Option<&'static PropertyDescriptor<'static>> {
|
|
let database = rbx_reflection_database::get().unwrap();
|
|
let mut current_class_name = class_name;
|
|
|
|
loop {
|
|
let class = database.classes.get(current_class_name)?;
|
|
if let Some(descriptor) = class.properties.get(prop_name) {
|
|
return Some(descriptor);
|
|
}
|
|
|
|
current_class_name = class.superclass?;
|
|
}
|
|
}
|
|
|
|
/// Outputs a string containing up to MAX_ITEMS entries from the given list. If
|
|
/// there are more than MAX_ITEMS items, the number of remaining items will be
|
|
/// listed.
|
|
fn nonexhaustive_list(values: &[&str]) -> String {
|
|
use std::fmt::Write;
|
|
|
|
const MAX_ITEMS: usize = 8;
|
|
|
|
let mut output = String::new();
|
|
|
|
let last_index = values.len() - 1;
|
|
let main_length = last_index.min(9);
|
|
|
|
let main_list = &values[..main_length];
|
|
for value in main_list {
|
|
output.push_str(value);
|
|
output.push_str(", ");
|
|
}
|
|
|
|
if values.len() > MAX_ITEMS {
|
|
write!(output, "or {} more", values.len() - main_length).unwrap();
|
|
} else {
|
|
output.push_str("or ");
|
|
output.push_str(values[values.len() - 1]);
|
|
}
|
|
|
|
output
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::json;
|
|
|
|
fn resolve(class: &str, prop: &str, json_value: &str) -> Variant {
|
|
let unresolved: UnresolvedValue = json::from_str(json_value).unwrap();
|
|
unresolved.resolve(class, prop).unwrap()
|
|
}
|
|
|
|
fn resolve_unambiguous(json_value: &str) -> Variant {
|
|
let unresolved: UnresolvedValue = json::from_str(json_value).unwrap();
|
|
unresolved.resolve_unambiguous().unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn bools() {
|
|
assert_eq!(resolve("BoolValue", "Value", "false"), Variant::Bool(false));
|
|
|
|
// Script.Disabled is inherited from BaseScript
|
|
assert_eq!(resolve("Script", "Disabled", "true"), Variant::Bool(true));
|
|
|
|
assert_eq!(resolve_unambiguous("false"), Variant::Bool(false));
|
|
assert_eq!(resolve_unambiguous("true"), Variant::Bool(true));
|
|
}
|
|
|
|
#[test]
|
|
fn strings() {
|
|
// String literals can stay as strings
|
|
assert_eq!(
|
|
resolve("StringValue", "Value", "\"Hello!\""),
|
|
Variant::String("Hello!".into()),
|
|
);
|
|
|
|
// String literals can also turn into ContentId
|
|
assert_eq!(
|
|
resolve("Sky", "MoonTextureId", "\"rbxassetid://12345\""),
|
|
Variant::ContentId("rbxassetid://12345".into()),
|
|
);
|
|
|
|
// String literals can turn into Content!
|
|
assert_eq!(
|
|
resolve(
|
|
"MeshPart",
|
|
"MeshContent",
|
|
"\"rbxasset://totally-a-real-uri.tiff\""
|
|
),
|
|
Variant::Content("rbxasset://totally-a-real-uri.tiff".into())
|
|
);
|
|
|
|
// What about BinaryString values? For forward-compatibility reasons, we
|
|
// don't support any shorthands for BinaryString.
|
|
//
|
|
// assert_eq!(
|
|
// resolve("Folder", "Tags", "\"a\\u0000b\\u0000c\""),
|
|
// Variant::BinaryString(b"a\0b\0c".to_vec().into()),
|
|
// );
|
|
|
|
assert_eq!(
|
|
resolve_unambiguous("\"Hello world!\""),
|
|
Variant::String("Hello world!".into()),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn numbers() {
|
|
assert_eq!(
|
|
resolve("Part", "CollisionGroupId", "123"),
|
|
Variant::Int32(123),
|
|
);
|
|
|
|
assert_eq!(
|
|
resolve("IntValue", "Value", "532413"),
|
|
Variant::Int64(532413),
|
|
);
|
|
|
|
assert_eq!(resolve("Part", "Transparency", "1"), Variant::Float32(1.0));
|
|
assert_eq!(resolve("NumberValue", "Value", "1"), Variant::Float64(1.0));
|
|
assert_eq!(
|
|
UnresolvedValue::from_variant(Variant::Float32(0.125), "Part", "Transparency")
|
|
.resolve("Part", "Transparency")
|
|
.unwrap(),
|
|
Variant::Float32(0.125),
|
|
);
|
|
|
|
assert_eq!(resolve_unambiguous("12.5"), Variant::Float64(12.5));
|
|
}
|
|
|
|
#[test]
|
|
fn vectors() {
|
|
assert_eq!(
|
|
resolve("ParticleEmitter", "SpreadAngle", "[1, 2]"),
|
|
Variant::Vector2(Vector2::new(1.0, 2.0)),
|
|
);
|
|
|
|
assert_eq!(
|
|
resolve("Part", "Position", "[4, 5, 6]"),
|
|
Variant::Vector3(Vector3::new(4.0, 5.0, 6.0)),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn colors() {
|
|
assert_eq!(
|
|
resolve("Part", "Color", "[1, 1, 1]"),
|
|
Variant::Color3(Color3::new(1.0, 1.0, 1.0)),
|
|
);
|
|
|
|
// There aren't any user-facing Color3uint8 properties. If there are
|
|
// some, we should treat them the same in the future.
|
|
}
|
|
|
|
#[test]
|
|
fn enums() {
|
|
assert_eq!(
|
|
resolve("Lighting", "Technology", "\"Voxel\""),
|
|
Variant::Enum(Enum::from_u32(1)),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn font() {
|
|
use rbx_dom_weak::types::{FontStyle, FontWeight};
|
|
|
|
assert_eq!(
|
|
resolve(
|
|
"TextLabel",
|
|
"FontFace",
|
|
r#"{"family": "rbxasset://fonts/families/RobotoMono.json", "weight": "Thin", "style": "Normal"}"#
|
|
),
|
|
Variant::Font(Font {
|
|
family: "rbxasset://fonts/families/RobotoMono.json".into(),
|
|
weight: FontWeight::Thin,
|
|
style: FontStyle::Normal,
|
|
cached_face_id: None,
|
|
})
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn material_colors() {
|
|
use rbx_dom_weak::types::{Color3uint8, TerrainMaterials};
|
|
|
|
let mut material_colors = MaterialColors::new();
|
|
material_colors.set_color(TerrainMaterials::Grass, Color3uint8::new(10, 20, 30));
|
|
material_colors.set_color(TerrainMaterials::Asphalt, Color3uint8::new(40, 50, 60));
|
|
material_colors.set_color(TerrainMaterials::LeafyGrass, Color3uint8::new(255, 155, 55));
|
|
|
|
assert_eq!(
|
|
resolve(
|
|
"Terrain",
|
|
"MaterialColors",
|
|
r#"{
|
|
"Grass": [10, 20, 30],
|
|
"Asphalt": [40, 50, 60],
|
|
"LeafyGrass": [255, 155, 55]
|
|
}"#
|
|
),
|
|
Variant::MaterialColors(material_colors)
|
|
)
|
|
}
|
|
}
|