Update rbx-dom (#1023)

This commit is contained in:
Micah
2025-04-02 11:32:27 -07:00
committed by GitHub
parent 0d6ff8ef8a
commit 833320de64
30 changed files with 8815 additions and 2249 deletions

View File

@@ -1,6 +1,10 @@
# Rojo Changelog
## Unreleased Changes
* Added support for Roblox's `Content` type. This replaces the old `Content` type with `ContentId` to reflect Roblox's change.
If you were previously using the fully-qualified syntax for `Content` you will need to switch it to `ContentId`.
* Added support for `Enum` attributes
* Significantly improved performance of `.rbxm` parsing
* Support for a `$schema` field in all special JSON files (`.project.json`, `.model.json`, and `.meta.json`) ([#974])
* Projects may now manually link `Ref` properties together using `Attributes`. ([#843])
This has two parts: using `id` or `$id` in JSON files or a `Rojo_Target` attribute, an Instance

178
Cargo.lock generated
View File

@@ -17,6 +17,19 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if 1.0.0",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@@ -171,6 +184,10 @@ name = "cc"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
dependencies = [
"jobserver",
"libc",
]
[[package]]
name = "cfg-if"
@@ -492,7 +509,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
]
@@ -935,6 +952,15 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "jod-thread"
version = "0.1.2"
@@ -962,9 +988,9 @@ dependencies = [
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lazycell"
@@ -986,7 +1012,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"bitflags 2.4.2",
"libc",
"redox_syscall",
"redox_syscall 0.4.1",
]
[[package]]
@@ -1001,6 +1027,16 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.21"
@@ -1241,6 +1277,29 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.5.10",
"smallvec",
"windows-targets 0.52.4",
]
[[package]]
name = "paste"
version = "1.0.14"
@@ -1310,6 +1369,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plotters"
version = "0.3.5"
@@ -1498,10 +1563,11 @@ dependencies = [
[[package]]
name = "rbx_binary"
version = "0.7.7"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b85057e8ff75a1ce99248200c4b3c7b481a3d52f921f1053ecd67921dcc7930"
checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296"
dependencies = [
"ahash",
"log",
"lz4",
"profiling",
@@ -1509,23 +1575,26 @@ dependencies = [
"rbx_reflection",
"rbx_reflection_database",
"thiserror",
"zstd",
]
[[package]]
name = "rbx_dom_weak"
version = "2.9.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcd2a17d09e46af0805f8b311a926402172b97e8d9388745c9adf8f448901841"
checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9"
dependencies = [
"ahash",
"rbx_types",
"serde",
"ustr",
]
[[package]]
name = "rbx_reflection"
version = "4.7.0"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8118ac6021d700e8debe324af6b40ecfd2cef270a00247849dbdfeebb0802677"
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
dependencies = [
"rbx_types",
"serde",
@@ -1534,9 +1603,9 @@ dependencies = [
[[package]]
name = "rbx_reflection_database"
version = "0.2.12+roblox-638"
version = "1.0.0+roblox-666"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e29381d675420e841f8c02db5755cbb2545ed3e13f56c539546dc58702b512a"
checksum = "a45b98a2794815736602087cf2fc9d85eb798e7c432d41307336014792768a46"
dependencies = [
"lazy_static",
"rbx_reflection",
@@ -1546,9 +1615,9 @@ dependencies = [
[[package]]
name = "rbx_types"
version = "1.10.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e30f49b2a3bb667e4074ba73c2dfb8ca0873f610b448ccf318a240acfdec6c73"
checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2"
dependencies = [
"base64 0.13.1",
"bitflags 1.3.2",
@@ -1561,10 +1630,11 @@ dependencies = [
[[package]]
name = "rbx_xml"
version = "0.13.5"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b14b3027bc9ccd82e2fc854c8bcd25ed58318e570c355bf2cf63df9cdbd5ba8"
checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c"
dependencies = [
"ahash",
"base64 0.13.1",
"log",
"rbx_dom_weak",
@@ -1582,6 +1652,15 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
dependencies = [
"bitflags 2.4.2",
]
[[package]]
name = "redox_users"
version = "0.4.4"
@@ -1896,6 +1975,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
@@ -2393,6 +2478,19 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "ustr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208"
dependencies = [
"ahash",
"byteorder",
"lazy_static",
"parking_lot",
"serde",
]
[[package]]
name = "uuid"
version = "1.7.0"
@@ -2784,3 +2882,51 @@ name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2 1.0.78",
"quote 1.0.35",
"syn 2.0.52",
]
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.15+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
dependencies = [
"cc",
"pkg-config",
]

View File

@@ -51,11 +51,11 @@ memofs = { version = "0.3.0", path = "crates/memofs" }
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
rbx_binary = "0.7.7"
rbx_dom_weak = "2.9.0"
rbx_reflection = "4.7.0"
rbx_reflection_database = "0.2.12"
rbx_xml = "0.13.5"
rbx_binary = "1.0.0"
rbx_dom_weak = "3.0.0"
rbx_reflection = "5.0.0"
rbx_reflection_database = "1.0.0"
rbx_xml = "1.0.0"
anyhow = "1.0.80"
backtrace = "0.3.69"

View File

@@ -188,6 +188,38 @@ types = {
},
Content = {
fromPod = function(pod): Content
if type(pod) == "string" then
if pod == "None" then
return Content.none
else
error(`unexpected Content value '{pod}'`)
end
else
local ty, value = next(pod)
if ty == "Uri" then
return Content.fromUri(value)
elseif ty == "Object" then
error("Object deserializing is not currently implemented")
else
error(`Unknown Content type '{ty}' (could not deserialize)`)
end
end
end,
toPod = function(roblox: Content)
if roblox.SourceType == Enum.ContentSourceType.None then
return "None"
elseif roblox.SourceType == Enum.ContentSourceType.Uri then
return { Uri = roblox.Uri }
elseif roblox.SourceType == Enum.ContentSourceType.Object then
error("Object serializing is not currently implemented")
else
error(`Unknown Content type '{roblox.SourceType} (could not serialize)`)
end
end,
},
ContentId = {
fromPod = identity,
toPod = identity,
},
@@ -205,6 +237,19 @@ types = {
end,
},
EnumItem = {
fromPod = function(pod)
return Enum[pod.type]:FromValue(pod.value)
end,
toPod = function(roblox)
return {
type = tostring(roblox.EnumType),
value = roblox.Value,
}
end,
},
Faces = {
fromPod = function(pod)
local faces = {}
@@ -300,7 +345,12 @@ types = {
local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope)
-- TODO: Add a test for NaN or Infinity values and envelopes
-- Right now it isn't possible because it'd fail the roundtrip.
-- It's more important that it works right now, though.
local value = keypoint.value or 0
local envelope = keypoint.envelope or 0
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, value, envelope)
end
return NumberSequence.new(keypoints)

View File

@@ -5,6 +5,7 @@ Error.Kind = {
UnknownProperty = "UnknownProperty",
PropertyNotReadable = "PropertyNotReadable",
PropertyNotWritable = "PropertyNotWritable",
CannotParseBinaryString = "CannotParseBinaryString",
Roblox = "Roblox",
}

View File

@@ -15,6 +15,12 @@
0.0
]
},
"TestEnumItem": {
"EnumItem": {
"type": "Material",
"value": 256
}
},
"TestNumber": {
"Float64": 1337.0
},
@@ -170,9 +176,23 @@
},
"ty": "ColorSequence"
},
"Content": {
"ContentId": {
"value": {
"Content": "rbxassetid://12345"
"ContentId": "rbxassetid://12345"
},
"ty": "ContentId"
},
"Content_None": {
"value": {
"Content": "None"
},
"ty": "Content"
},
"Content_Uri": {
"value": {
"Content": {
"Uri": "rbxasset://abc/123.rojo"
}
},
"ty": "Content"
},
@@ -182,6 +202,15 @@
},
"ty": "Enum"
},
"EnumItem": {
"value": {
"EnumItem": {
"type": "Material",
"value": 256
}
},
"ty": "EnumItem"
},
"Faces": {
"value": {
"Faces": [

View File

@@ -1,6 +1,8 @@
local CollectionService = game:GetService("CollectionService")
local ScriptEditorService = game:GetService("ScriptEditorService")
local Error = require(script.Parent.Error)
--- A list of `Enum.Material` values that are used for Terrain.MaterialColors
local TERRAIN_MATERIAL_COLORS = {
Enum.Material.Grass,
@@ -51,6 +53,10 @@ return {
return true, instance:GetAttributes()
end,
write = function(instance, _, value)
if typeof(value) ~= "table" then
return false, Error.new(Error.Kind.CannotParseBinaryString)
end
local existing = instance:GetAttributes()
local didAllWritesSucceed = true
@@ -160,9 +166,14 @@ return {
return true, colors
end,
write = function(instance: Terrain, _, value: { [Enum.Material]: Color3 })
if typeof(value) ~= "table" then
return false, Error.new(Error.Kind.CannotParseBinaryString)
end
for material, color in value do
instance:SetMaterialColor(material, color)
end
return true
end,
},

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ use clap::Parser;
use fs_err::File;
use memofs::Vfs;
use rayon::prelude::*;
use rbx_dom_weak::types::Ref;
use rbx_dom_weak::{types::Ref, Ustr};
use serde::Serialize;
use tokio::runtime::Runtime;
@@ -26,7 +26,7 @@ const PATH_STRIP_FAILED_ERR: &str = "Failed to create relative paths for project
#[serde(rename_all = "camelCase")]
struct SourcemapNode<'a> {
name: &'a str,
class_name: &'a str,
class_name: Ustr,
#[serde(skip_serializing_if = "Vec::is_empty")]
file_paths: Vec<PathBuf>,
@@ -113,7 +113,7 @@ fn filter_nothing(_instance: &InstanceWithMeta) -> bool {
fn filter_non_scripts(instance: &InstanceWithMeta) -> bool {
matches!(
instance.class_name(),
instance.class_name().as_str(),
"Script" | "LocalScript" | "ModuleScript"
)
}

View File

@@ -7,6 +7,7 @@ use std::{
};
use memofs::Vfs;
use rbx_dom_weak::{Ustr, UstrMap};
use serde::{Deserialize, Serialize};
use thiserror::Error;
@@ -310,7 +311,7 @@ pub struct ProjectNode {
/// `$className` CANNOT be set if `$path` is set and the instance described
/// by that path has a ClassName other than Folder.
#[serde(rename = "$className", skip_serializing_if = "Option::is_none")]
pub class_name: Option<String>,
pub class_name: Option<Ustr>,
/// If set, defines an ID for the described Instance that can be used
/// to refer to it for the purpose of referent properties.
@@ -329,7 +330,7 @@ pub struct ProjectNode {
default,
skip_serializing_if = "HashMap::is_empty"
)]
pub properties: HashMap<String, UnresolvedValue>,
pub properties: UstrMap<UnresolvedValue>,
#[serde(
rename = "$attributes",

View File

@@ -2,8 +2,8 @@ use std::borrow::Borrow;
use anyhow::{bail, format_err};
use rbx_dom_weak::types::{
Attributes, CFrame, Color3, Content, Enum, Font, MaterialColors, Matrix3, Tags, Variant,
VariantType, Vector2, Vector3,
Attributes, CFrame, Color3, Content, ContentId, Enum, Font, MaterialColors, Matrix3, Tags,
Variant, VariantType, Vector2, Vector3,
};
use rbx_reflection::{DataType, PropertyDescriptor};
use serde::{Deserialize, Serialize};
@@ -116,6 +116,9 @@ impl AmbiguousValue {
(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] as f32, value[1] as f32).into())
@@ -275,10 +278,20 @@ mod test {
Variant::String("Hello!".into()),
);
// String literals can also turn into Content
// String literals can also turn into ContentId
assert_eq!(
resolve("Sky", "MoonTextureId", "\"rbxassetid://12345\""),
Variant::Content("rbxassetid://12345".into()),
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

View File

@@ -1,10 +1,10 @@
//! Defines the structure of an instance snapshot.
use std::{borrow::Cow, collections::HashMap};
use std::borrow::Cow;
use rbx_dom_weak::{
types::{Ref, Variant},
Instance, WeakDom,
ustr, AHashMap, HashMapExt as _, Instance, Ustr, UstrMap, WeakDom,
};
use serde::{Deserialize, Serialize};
@@ -27,10 +27,10 @@ pub struct InstanceSnapshot {
pub name: Cow<'static, str>,
/// Corresponds to the ClassName property of the instance.
pub class_name: Cow<'static, str>,
pub class_name: Ustr,
/// All other properties of the instance, weakly-typed.
pub properties: HashMap<String, Variant>,
pub properties: UstrMap<Variant>,
/// The children of the instance represented as more snapshots.
///
@@ -44,8 +44,8 @@ impl InstanceSnapshot {
snapshot_id: Ref::none(),
metadata: InstanceMetadata::default(),
name: Cow::Borrowed("DEFAULT"),
class_name: Cow::Borrowed("DEFAULT"),
properties: HashMap::new(),
class_name: ustr("DEFAULT"),
properties: UstrMap::new(),
children: Vec::new(),
}
}
@@ -57,23 +57,23 @@ impl InstanceSnapshot {
}
}
pub fn class_name(self, class_name: impl Into<String>) -> Self {
pub fn class_name<S: Into<Ustr>>(self, class_name: S) -> Self {
Self {
class_name: Cow::Owned(class_name.into()),
class_name: class_name.into(),
..self
}
}
pub fn property<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<String>,
K: Into<Ustr>,
V: Into<Variant>,
{
self.properties.insert(key.into(), value.into());
self
}
pub fn properties(self, properties: impl Into<HashMap<String, Variant>>) -> Self {
pub fn properties(self, properties: impl Into<UstrMap<Variant>>) -> Self {
Self {
properties: properties.into(),
..self
@@ -107,7 +107,7 @@ impl InstanceSnapshot {
Self::from_raw_tree(&mut raw_tree, id)
}
fn from_raw_tree(raw_tree: &mut HashMap<Ref, Instance>, id: Ref) -> Self {
fn from_raw_tree(raw_tree: &mut AHashMap<Ref, Instance>, id: Ref) -> Self {
let instance = raw_tree
.remove(&id)
.expect("instance did not exist in tree");
@@ -122,7 +122,7 @@ impl InstanceSnapshot {
snapshot_id: id,
metadata: InstanceMetadata::default(),
name: Cow::Owned(instance.name),
class_name: Cow::Owned(instance.class),
class_name: instance.class,
properties: instance.properties,
children,
}

View File

@@ -1,8 +1,9 @@
//! Defines the data structures used for describing instance patches.
use std::collections::HashMap;
use rbx_dom_weak::types::{Ref, Variant};
use rbx_dom_weak::{
types::{Ref, Variant},
HashMapExt as _, Ustr, UstrMap,
};
use serde::{Deserialize, Serialize};
use super::{InstanceMetadata, InstanceSnapshot};
@@ -41,11 +42,11 @@ pub struct PatchAdd {
pub struct PatchUpdate {
pub id: Ref,
pub changed_name: Option<String>,
pub changed_class_name: Option<String>,
pub changed_class_name: Option<Ustr>,
/// Contains all changed properties. If a property is assigned to `None`,
/// then that property has been removed.
pub changed_properties: HashMap<String, Option<Variant>>,
pub changed_properties: UstrMap<Option<Variant>>,
/// Changed Rojo-specific metadata, if any of it changed.
pub changed_metadata: Option<InstanceMetadata>,
@@ -88,8 +89,8 @@ pub struct AppliedPatchUpdate {
// TODO: Store previous values in order to detect application conflicts
pub changed_name: Option<String>,
pub changed_class_name: Option<String>,
pub changed_properties: HashMap<String, Option<Variant>>,
pub changed_class_name: Option<Ustr>,
pub changed_properties: UstrMap<Option<Variant>>,
pub changed_metadata: Option<InstanceMetadata>,
}
@@ -99,7 +100,7 @@ impl AppliedPatchUpdate {
id,
changed_name: None,
changed_class_name: None,
changed_properties: HashMap::new(),
changed_properties: UstrMap::new(),
changed_metadata: None,
}
}

View File

@@ -5,7 +5,10 @@ use std::{
mem::take,
};
use rbx_dom_weak::types::{Ref, Variant};
use rbx_dom_weak::{
types::{Ref, Variant},
ustr, Ustr,
};
use super::{
patch::{AppliedPatchSet, AppliedPatchUpdate, PatchSet, PatchUpdate},
@@ -76,7 +79,7 @@ struct PatchApplyContext {
/// Tracks all ref properties that were specified using attributes. This has
/// to be handled after everything else is done just like normal referent
/// properties.
attribute_refs_to_rewrite: MultiMap<Ref, (String, String)>,
attribute_refs_to_rewrite: MultiMap<Ref, (Ustr, String)>,
/// The current applied patch result, describing changes made to the tree.
applied_patch_set: AppliedPatchSet,
@@ -193,7 +196,7 @@ fn apply_update_child(context: &mut PatchApplyContext, tree: &mut RojoTree, patc
}
if let Some(class_name) = patch.changed_class_name {
*instance.class_name_mut() = class_name.clone();
instance.set_class_name(class_name);
applied_patch.changed_class_name = Some(class_name);
}
@@ -219,10 +222,10 @@ fn apply_update_child(context: &mut PatchApplyContext, tree: &mut RojoTree, patc
instance
.properties_mut()
.insert(key.clone(), Variant::Ref(new_referent));
.insert(key, Variant::Ref(new_referent));
}
Some(ref value) => {
instance.properties_mut().insert(key.clone(), value.clone());
instance.properties_mut().insert(key, value.clone());
}
None => {
instance.properties_mut().remove(&key);
@@ -247,7 +250,7 @@ fn defer_ref_properties(tree: &mut RojoTree, id: Ref, context: &mut PatchApplyCo
let instance = tree
.get_instance(id)
.expect("Instances should exist when calculating deferred refs");
let attributes = match instance.properties().get("Attributes") {
let attributes = match instance.properties().get(&ustr("Attributes")) {
Some(Variant::Attributes(attrs)) => attrs,
_ => return,
};
@@ -275,12 +278,12 @@ fn defer_ref_properties(tree: &mut RojoTree, id: Ref, context: &mut PatchApplyCo
if let Variant::String(prop_value) = attr_value {
context
.attribute_refs_to_rewrite
.insert(id, (prop_name.to_owned(), prop_value.clone()));
.insert(id, (ustr(prop_name), prop_value.clone()));
} else if let Variant::BinaryString(prop_value) = attr_value {
if let Ok(str) = std::str::from_utf8(prop_value.as_ref()) {
context
.attribute_refs_to_rewrite
.insert(id, (prop_name.to_owned(), str.to_string()));
.insert(id, (ustr(prop_name), str.to_string()));
} else {
log::error!("IDs specified by referent property attributes must be valid UTF-8 strings.")
}
@@ -304,7 +307,7 @@ mod test {
use std::borrow::Cow;
use rbx_dom_weak::types::Variant;
use rbx_dom_weak::{types::Variant, UstrMap};
use super::super::PatchAdd;
@@ -320,8 +323,8 @@ mod test {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("Foo"),
class_name: Cow::Borrowed("Bar"),
properties: [("Baz".to_owned(), Variant::Int32(5))].into(),
class_name: ustr("Bar"),
properties: UstrMap::from_iter([(ustr("Baz"), Variant::Int32(5))]),
children: Vec::new(),
};
@@ -340,7 +343,7 @@ mod test {
let child_instance = tree.get_instance(child_id).unwrap();
assert_eq!(child_instance.name(), &snapshot.name);
assert_eq!(child_instance.class_name(), &snapshot.class_name);
assert_eq!(child_instance.class_name(), snapshot.class_name);
assert_eq!(child_instance.properties(), &snapshot.properties);
assert!(child_instance.children().is_empty());
}
@@ -363,16 +366,15 @@ mod test {
let patch = PatchUpdate {
id: root_id,
changed_name: Some("Foo".to_owned()),
changed_class_name: Some("NewClassName".to_owned()),
changed_properties: [
changed_class_name: Some(ustr("NewClassName")),
changed_properties: UstrMap::from_iter([
// The value of Foo has changed
("Foo".to_owned(), Some(Variant::Int32(8))),
(ustr("Foo"), Some(Variant::Int32(8))),
// Bar has been deleted
("Bar".to_owned(), None),
(ustr("Bar"), None),
// Baz has been added
("Baz".to_owned(), Some(Variant::Int32(10))),
]
.into(),
(ustr("Baz"), Some(Variant::Int32(10))),
]),
changed_metadata: None,
};
@@ -383,12 +385,11 @@ mod test {
apply_patch_set(&mut tree, patch_set);
let expected_properties = [
("Foo".to_owned(), Variant::Int32(8)),
("Baz".to_owned(), Variant::Int32(10)),
("Unchanged".to_owned(), Variant::Int32(-5)),
]
.into();
let expected_properties = UstrMap::from_iter([
(ustr("Foo"), Variant::Int32(8)),
(ustr("Baz"), Variant::Int32(10)),
(ustr("Unchanged"), Variant::Int32(-5)),
]);
let root_instance = tree.get_instance(root_id).unwrap();
assert_eq!(root_instance.name(), "Foo");

View File

@@ -1,12 +1,12 @@
//! Defines the algorithm for computing a roughly-minimal patch set given an
//! existing instance tree and an instance snapshot.
use std::{
collections::{HashMap, HashSet},
mem::take,
};
use std::{collections::HashMap, mem::take};
use rbx_dom_weak::types::{Ref, Variant};
use rbx_dom_weak::{
types::{Ref, Variant},
ustr, HashMapExt as _, UstrMap, UstrSet,
};
use crate::{RojoRef, REF_POINTER_ATTRIBUTE_PREFIX};
@@ -99,8 +99,8 @@ fn compute_property_patches(
patch_set: &mut PatchSet,
tree: &RojoTree,
) {
let mut visited_properties = HashSet::new();
let mut changed_properties = HashMap::new();
let mut visited_properties = UstrSet::default();
let mut changed_properties = UstrMap::new();
let attribute_ref_properties = compute_ref_properties(snapshot, tree);
@@ -113,7 +113,7 @@ fn compute_property_patches(
let changed_class_name = if snapshot.class_name == instance.class_name() {
None
} else {
Some(take(&mut snapshot.class_name).into_owned())
Some(take(&mut snapshot.class_name))
};
let changed_metadata = if &snapshot.metadata == instance.metadata() {
@@ -123,7 +123,7 @@ fn compute_property_patches(
};
for (name, snapshot_value) in take(&mut snapshot.properties) {
visited_properties.insert(name.clone());
visited_properties.insert(name);
match instance.properties().get(&name) {
Some(instance_value) => {
@@ -138,11 +138,11 @@ fn compute_property_patches(
}
for name in instance.properties().keys() {
if visited_properties.contains(name.as_str()) {
if visited_properties.contains(name) {
continue;
}
changed_properties.insert(name.clone(), None);
changed_properties.insert(*name, None);
}
for (name, ref_value) in attribute_ref_properties {
@@ -250,9 +250,9 @@ fn compute_children_patches(
fn compute_ref_properties(
snapshot: &InstanceSnapshot,
tree: &RojoTree,
) -> HashMap<String, Option<Variant>> {
let mut map = HashMap::new();
let attributes = match snapshot.properties.get("Attributes") {
) -> UstrMap<Option<Variant>> {
let mut map = UstrMap::new();
let attributes = match snapshot.properties.get(&ustr("Attributes")) {
Some(Variant::Attributes(attrs)) => attrs,
_ => return map,
};
@@ -284,9 +284,9 @@ fn compute_ref_properties(
}
};
if let Some(target_id) = tree.get_specified_id(&rojo_ref) {
map.insert(prop_name.to_string(), Some(Variant::Ref(target_id)));
map.insert(ustr(prop_name), Some(Variant::Ref(target_id)));
} else {
map.insert(prop_name.to_string(), None);
map.insert(ustr(prop_name), None);
}
}
@@ -314,11 +314,11 @@ mod test {
let snapshot_id = Ref::new();
let snapshot = InstanceSnapshot {
snapshot_id,
properties: [("Self".to_owned(), Variant::Ref(snapshot_id))].into(),
properties: UstrMap::from_iter([(ustr("Self"), Variant::Ref(snapshot_id))]),
metadata: Default::default(),
name: Cow::Borrowed("foo"),
class_name: Cow::Borrowed("foo"),
class_name: ustr("foo"),
children: Vec::new(),
};
@@ -329,7 +329,10 @@ mod test {
id: root_id,
changed_name: None,
changed_class_name: None,
changed_properties: [("Self".to_owned(), Some(Variant::Ref(root_id)))].into(),
changed_properties: UstrMap::from_iter([(
ustr("Self"),
Some(Variant::Ref(root_id)),
)]),
changed_metadata: None,
}],
added_instances: Vec::new(),
@@ -353,19 +356,19 @@ mod test {
let snapshot = InstanceSnapshot {
snapshot_id,
children: vec![InstanceSnapshot {
properties: [("Self".to_owned(), Variant::Ref(snapshot_id))].into(),
properties: UstrMap::from_iter([(ustr("Self"), Variant::Ref(snapshot_id))]),
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("child"),
class_name: Cow::Borrowed("child"),
class_name: ustr("child"),
children: Vec::new(),
}],
metadata: Default::default(),
properties: HashMap::new(),
properties: UstrMap::new(),
name: Cow::Borrowed("foo"),
class_name: Cow::Borrowed("foo"),
class_name: ustr("foo"),
};
let patch_set = compute_patch_set(Some(snapshot), &tree, root_id);
@@ -376,9 +379,9 @@ mod test {
instance: InstanceSnapshot {
snapshot_id: Ref::none(),
metadata: Default::default(),
properties: [("Self".to_owned(), Variant::Ref(root_id))].into(),
properties: UstrMap::from_iter([(ustr("Self"), Variant::Ref(root_id))]),
name: Cow::Borrowed("child"),
class_name: Cow::Borrowed("child"),
class_name: ustr("child"),
children: Vec::new(),
},
}],

View File

@@ -1,5 +1,6 @@
use insta::assert_yaml_snapshot;
use rbx_dom_weak::{ustr, UstrMap};
use rojo_insta_ext::RedactionMap;
use crate::{
@@ -18,7 +19,7 @@ fn set_name_and_class_name() {
updated_instances: vec![PatchUpdate {
id: tree.get_root_id(),
changed_name: Some("Hello, world!".to_owned()),
changed_class_name: Some("Folder".to_owned()),
changed_class_name: Some(ustr("Folder")),
changed_properties: Default::default(),
changed_metadata: None,
}],
@@ -46,7 +47,7 @@ fn add_property() {
id: tree.get_root_id(),
changed_name: None,
changed_class_name: None,
changed_properties: [("Foo".to_owned(), Some("Value of Foo".into()))].into(),
changed_properties: UstrMap::from_iter([(ustr("Foo"), Some("Value of Foo".into()))]),
changed_metadata: None,
}],
..Default::default()
@@ -74,7 +75,7 @@ fn remove_property() {
root_instance
.properties_mut()
.insert("Foo".to_owned(), "Should be removed".into());
.insert(ustr("Foo"), "Should be removed".into());
}
let tree_view = view_tree(&tree, &mut redactions);
@@ -85,7 +86,7 @@ fn remove_property() {
id: tree.get_root_id(),
changed_name: None,
changed_class_name: None,
changed_properties: [("Foo".to_owned(), None)].into(),
changed_properties: UstrMap::from_iter([(ustr("Foo"), None)]),
changed_metadata: None,
}],
..Default::default()

View File

@@ -2,7 +2,7 @@ use std::borrow::Cow;
use insta::assert_yaml_snapshot;
use rbx_dom_weak::types::Ref;
use rbx_dom_weak::{types::Ref, ustr, UstrMap};
use rojo_insta_ext::RedactionMap;
use crate::snapshot::{compute_patch_set, InstanceSnapshot, RojoTree};
@@ -18,7 +18,7 @@ fn set_name_and_class_name() {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("Some Folder"),
class_name: Cow::Borrowed("Folder"),
class_name: ustr("Folder"),
properties: Default::default(),
children: Vec::new(),
};
@@ -40,8 +40,8 @@ fn set_property() {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("ROOT"),
class_name: Cow::Borrowed("ROOT"),
properties: [("PropertyName".into(), "Hello, world!".into())].into(),
class_name: ustr("ROOT"),
properties: UstrMap::from_iter([(ustr("PropertyName"), "Hello, world!".into())]),
children: Vec::new(),
};
@@ -61,17 +61,16 @@ fn remove_property() {
{
let root_id = tree.get_root_id();
let mut root_instance = tree.get_instance_mut(root_id).unwrap();
root_instance.properties_mut().insert(
"Foo".to_owned(),
"This should be removed by the patch.".into(),
);
root_instance
.properties_mut()
.insert(ustr("Foo"), "This should be removed by the patch.".into());
}
let snapshot = InstanceSnapshot {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("ROOT"),
class_name: Cow::Borrowed("ROOT"),
class_name: ustr("ROOT"),
properties: Default::default(),
children: Vec::new(),
};
@@ -93,13 +92,13 @@ fn add_child() {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("ROOT"),
class_name: Cow::Borrowed("ROOT"),
class_name: ustr("ROOT"),
properties: Default::default(),
children: vec![InstanceSnapshot {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("New"),
class_name: Cow::Borrowed("Folder"),
class_name: ustr("Folder"),
properties: Default::default(),
children: Vec::new(),
}],
@@ -132,7 +131,7 @@ fn remove_child() {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Borrowed("ROOT"),
class_name: Cow::Borrowed("ROOT"),
class_name: ustr("ROOT"),
properties: Default::default(),
children: Vec::new(),
};

View File

@@ -5,7 +5,7 @@ use std::{
use rbx_dom_weak::{
types::{Ref, Variant},
Instance, InstanceBuilder, WeakDom,
ustr, Instance, InstanceBuilder, Ustr, UstrMap, WeakDom,
};
use crate::{multimap::MultiMap, RojoRef};
@@ -95,7 +95,7 @@ impl RojoTree {
pub fn insert_instance(&mut self, parent_ref: Ref, snapshot: InstanceSnapshot) -> Ref {
let builder = InstanceBuilder::empty()
.with_class(snapshot.class_name.into_owned())
.with_class(snapshot.class_name)
.with_name(snapshot.name.into_owned())
.with_properties(snapshot.properties);
@@ -283,11 +283,11 @@ impl<'a> InstanceWithMeta<'a> {
&self.instance.name
}
pub fn class_name(&self) -> &'a str {
&self.instance.class
pub fn class_name(&self) -> Ustr {
self.instance.class
}
pub fn properties(&self) -> &'a HashMap<String, Variant> {
pub fn properties(&self) -> &'a UstrMap<Variant> {
&self.instance.properties
}
@@ -328,15 +328,15 @@ impl InstanceWithMetaMut<'_> {
&self.instance.class
}
pub fn class_name_mut(&mut self) -> &mut String {
&mut self.instance.class
pub fn set_class_name<'a, S: Into<&'a str>>(&mut self, new_class: S) {
self.instance.class = ustr(new_class.into());
}
pub fn properties(&self) -> &HashMap<String, Variant> {
pub fn properties(&self) -> &UstrMap<Variant> {
&self.instance.properties
}
pub fn properties_mut(&mut self) -> &mut HashMap<String, Variant> {
pub fn properties_mut(&mut self) -> &mut UstrMap<Variant> {
&mut self.instance.properties
}

View File

@@ -2,6 +2,7 @@ use std::{collections::BTreeMap, path::Path};
use anyhow::Context;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::ustr;
use serde::Serialize;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -30,7 +31,7 @@ pub fn snapshot_csv(
let mut snapshot = InstanceSnapshot::new()
.name(name)
.class_name("LocalizationTable")
.properties([("Contents".to_owned(), table_contents.into())])
.property(ustr("Contents"), table_contents)
.metadata(
InstanceMetadata::new()
.instigating_source(path)

View File

@@ -2,6 +2,7 @@ use std::path::Path;
use anyhow::Context;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::ustr;
use crate::{
lua_ast::{Expression, Statement},
@@ -23,14 +24,12 @@ pub fn snapshot_json(
let as_lua = json_to_lua(value).to_string();
let properties = [("Source".to_owned(), as_lua.into())];
let meta_path = path.with_file_name(format!("{}.meta.json", name));
let mut snapshot = InstanceSnapshot::new()
.name(name)
.class_name("ModuleScript")
.properties(properties)
.property(ustr("Source"), as_lua)
.metadata(
InstanceMetadata::new()
.instigating_source(path)

View File

@@ -2,7 +2,10 @@ use std::{borrow::Cow, collections::HashMap, path::Path, str};
use anyhow::Context;
use memofs::Vfs;
use rbx_dom_weak::types::{Attributes, Ref};
use rbx_dom_weak::{
types::{Attributes, Ref},
HashMapExt as _, Ustr, UstrMap,
};
use serde::Deserialize;
use crate::{
@@ -68,7 +71,7 @@ struct JsonModel {
name: Option<String>,
#[serde(alias = "ClassName")]
class_name: String,
class_name: Ustr,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
@@ -82,10 +85,10 @@ struct JsonModel {
#[serde(
alias = "Properties",
default = "HashMap::new",
default = "UstrMap::new",
skip_serializing_if = "HashMap::is_empty"
)]
properties: HashMap<String, UnresolvedValue>,
properties: UstrMap<UnresolvedValue>,
#[serde(default = "HashMap::new", skip_serializing_if = "HashMap::is_empty")]
attributes: HashMap<String, UnresolvedValue>,
@@ -93,7 +96,7 @@ struct JsonModel {
impl JsonModel {
fn into_snapshot(self) -> anyhow::Result<InstanceSnapshot> {
let name = self.name.unwrap_or_else(|| self.class_name.clone());
let name = self.name.unwrap_or_else(|| self.class_name.to_owned());
let class_name = self.class_name;
let mut children = Vec::with_capacity(self.children.len());
@@ -101,7 +104,7 @@ impl JsonModel {
children.push(child.into_snapshot()?);
}
let mut properties = HashMap::with_capacity(self.properties.len());
let mut properties = UstrMap::with_capacity(self.properties.len());
for (key, unresolved) in self.properties {
let value = unresolved.resolve(&class_name, &key)?;
properties.insert(key, value);
@@ -122,7 +125,7 @@ impl JsonModel {
snapshot_id: Ref::none(),
metadata: Default::default(),
name: Cow::Owned(name),
class_name: Cow::Owned(class_name),
class_name,
properties,
children,
})

View File

@@ -1,7 +1,7 @@
use std::{collections::HashMap, path::Path, str};
use std::{path::Path, str};
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::types::Enum;
use rbx_dom_weak::{types::Enum, ustr, HashMapExt as _, UstrMap};
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -42,12 +42,12 @@ pub fn snapshot_lua(
let contents = vfs.read_to_string_lf_normalized(path)?;
let contents_str = contents.as_str();
let mut properties = HashMap::with_capacity(2);
properties.insert("Source".to_owned(), contents_str.into());
let mut properties = UstrMap::with_capacity(2);
properties.insert(ustr("Source"), contents_str.into());
if let Some(run_context) = run_context {
properties.insert(
"RunContext".to_owned(),
ustr("RunContext"),
Enum::from_u32(run_context.to_owned()).into(),
);
}

View File

@@ -1,7 +1,7 @@
use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use std::{collections::HashMap, path::PathBuf};
use anyhow::{format_err, Context};
use rbx_dom_weak::types::Attributes;
use rbx_dom_weak::{types::Attributes, Ustr, UstrMap};
use serde::{Deserialize, Serialize};
use crate::{resolution::UnresolvedValue, snapshot::InstanceSnapshot, RojoRef};
@@ -23,7 +23,7 @@ pub struct AdjacentMetadata {
pub ignore_unknown_instances: Option<bool>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub properties: HashMap<String, UnresolvedValue>,
pub properties: UstrMap<UnresolvedValue>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub attributes: HashMap<String, UnresolvedValue>,
@@ -117,13 +117,13 @@ pub struct DirectoryMetadata {
pub ignore_unknown_instances: Option<bool>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub properties: HashMap<String, UnresolvedValue>,
pub properties: UstrMap<UnresolvedValue>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub attributes: HashMap<String, UnresolvedValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub class_name: Option<String>,
pub class_name: Option<Ustr>,
#[serde(skip)]
pub path: PathBuf,
@@ -161,7 +161,7 @@ impl DirectoryMetadata {
));
}
snapshot.class_name = Cow::Owned(class_name);
snapshot.class_name = class_name;
}
Ok(())

View File

@@ -1,8 +1,11 @@
use std::{borrow::Cow, collections::HashMap, path::Path};
use std::{borrow::Cow, path::Path};
use anyhow::{bail, Context};
use memofs::Vfs;
use rbx_dom_weak::types::{Attributes, Ref};
use rbx_dom_weak::{
types::{Attributes, Ref},
ustr, HashMapExt as _, Ustr, UstrMap,
};
use rbx_reflection::ClassTag;
use crate::{
@@ -88,14 +91,10 @@ pub fn snapshot_project_node(
) -> anyhow::Result<Option<InstanceSnapshot>> {
let project_folder = project_path.parent().unwrap();
let class_name_from_project = node
.class_name
.as_ref()
.map(|name| Cow::Owned(name.clone()));
let mut class_name_from_path = None;
let name = Cow::Owned(instance_name.to_owned());
let mut properties = HashMap::new();
let mut properties = UstrMap::new();
let mut children = Vec::new();
let mut metadata = InstanceMetadata::new().context(context);
@@ -136,7 +135,7 @@ pub fn snapshot_project_node(
let class_name_from_inference = infer_class_name(&name, parent_class);
let class_name = match (
class_name_from_project,
node.class_name,
class_name_from_path,
class_name_from_inference,
&node.path,
@@ -249,7 +248,7 @@ pub fn snapshot_project_node(
_ => {}
}
properties.insert(key.clone(), value);
properties.insert(*key, value);
}
if !node.attributes.is_empty() {
@@ -304,7 +303,7 @@ pub fn snapshot_project_node(
}))
}
fn infer_class_name(name: &str, parent_class: Option<&str>) -> Option<Cow<'static, str>> {
fn infer_class_name(name: &str, parent_class: Option<&str>) -> Option<Ustr> {
// If className wasn't defined from another source, we may be able
// to infer one.
@@ -317,18 +316,18 @@ fn infer_class_name(name: &str, parent_class: Option<&str>) -> Option<Cow<'stati
let descriptor = rbx_reflection_database::get().classes.get(name)?;
if descriptor.tags.contains(&ClassTag::Service) {
return Some(Cow::Owned(name.to_owned()));
return Some(ustr(name));
}
} else if parent_class == "StarterPlayer" {
// StarterPlayer has two special members with their own classes.
if name == "StarterPlayerScripts" || name == "StarterCharacterScripts" {
return Some(Cow::Owned(name.to_owned()));
return Some(ustr(name));
}
} else if parent_class == "Workspace" {
// Workspace has a special Terrain class inside it
if name == "Terrain" {
return Some(Cow::Owned(name.to_owned()));
return Some(ustr(name));
}
}

View File

@@ -2,6 +2,7 @@ use std::path::Path;
use anyhow::Context;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::ustr;
use crate::{
lua_ast::{Expression, Statement},
@@ -23,14 +24,12 @@ pub fn snapshot_toml(
let as_lua = toml_to_lua(value).to_string();
let properties = [("Source".to_owned(), as_lua.into())];
let meta_path = path.with_file_name(format!("{}.meta.json", name));
let mut snapshot = InstanceSnapshot::new()
.name(name)
.class_name("ModuleScript")
.properties(properties)
.property(ustr("Source"), as_lua)
.metadata(
InstanceMetadata::new()
.instigating_source(path)

View File

@@ -1,6 +1,7 @@
use std::{path::Path, str};
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::ustr;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -15,14 +16,12 @@ pub fn snapshot_txt(
let contents = vfs.read_to_string(path)?;
let contents_str = contents.as_str();
let properties = [("Value".to_owned(), contents_str.into())];
let meta_path = path.with_file_name(format!("{}.meta.json", name));
let mut snapshot = InstanceSnapshot::new()
.name(name)
.class_name("StringValue")
.properties(properties)
.property(ustr("Value"), contents_str)
.metadata(
InstanceMetadata::new()
.instigating_source(path)

View File

@@ -1,9 +1,10 @@
//! Utiilty that helps redact nondeterministic information from trees so that
//! they can be part of snapshot tests.
use std::collections::HashMap;
use rbx_dom_weak::types::{Ref, Variant};
use rbx_dom_weak::{
types::{Ref, Variant},
Ustr, UstrMap,
};
use rojo_insta_ext::RedactionMap;
use serde::Serialize;
@@ -34,8 +35,8 @@ pub fn intern_tree(tree: &RojoTree, redactions: &mut RedactionMap) {
struct InstanceView {
id: Ref,
name: String,
class_name: String,
properties: HashMap<String, Variant>,
class_name: Ustr,
properties: UstrMap<Variant>,
metadata: InstanceMetadata,
children: Vec<InstanceView>,
}
@@ -46,7 +47,7 @@ fn extract_instance_view(tree: &RojoTree, id: Ref) -> InstanceView {
InstanceView {
id: instance.id(),
name: instance.name().to_owned(),
class_name: instance.class_name().to_owned(),
class_name: instance.class_name(),
properties: instance.properties().clone(),
metadata: instance.metadata().clone(),
children: instance

View File

@@ -280,7 +280,7 @@ impl ApiService {
/// If this instance is represented by a script, try to find the correct .lua or .luau
/// file to open to edit it.
fn pick_script_path(instance: InstanceWithMeta<'_>) -> Option<PathBuf> {
match instance.class_name() {
match instance.class_name().as_str() {
"Script" | "LocalScript" | "ModuleScript" => {}
_ => return None,
}

View File

@@ -7,7 +7,10 @@ use std::{
collections::{HashMap, HashSet},
};
use rbx_dom_weak::types::{Ref, Variant, VariantType};
use rbx_dom_weak::{
types::{Ref, Variant, VariantType},
Ustr, UstrMap,
};
use serde::{Deserialize, Serialize};
use crate::{
@@ -84,12 +87,12 @@ impl<'a> SubscribeMessage<'a> {
pub struct InstanceUpdate {
pub id: Ref,
pub changed_name: Option<String>,
pub changed_class_name: Option<String>,
pub changed_class_name: Option<Ustr>,
// TODO: Transform from HashMap<String, Option<_>> to something else, since
// TODO: Transform from UstrMap<String, Option<_>> to something else, since
// null will get lost when decoding from JSON in some languages.
#[serde(default)]
pub changed_properties: HashMap<String, Option<Variant>>,
pub changed_properties: UstrMap<Option<Variant>>,
pub changed_metadata: Option<InstanceMetadata>,
}
@@ -113,8 +116,8 @@ pub struct Instance<'a> {
pub id: Ref,
pub parent: Ref,
pub name: Cow<'a, str>,
pub class_name: Cow<'a, str>,
pub properties: HashMap<String, Cow<'a, Variant>>,
pub class_name: Ustr,
pub properties: UstrMap<Cow<'a, Variant>>,
pub children: Cow<'a, [Ref]>,
pub metadata: Option<InstanceMetadata>,
}
@@ -125,14 +128,14 @@ impl Instance<'_> {
.properties()
.iter()
.filter(|(_key, value)| property_filter(Some(value)))
.map(|(key, value)| (key.clone(), Cow::Borrowed(value)))
.map(|(key, value)| (*key, Cow::Borrowed(value)))
.collect();
Instance {
id: source.id(),
parent: source.parent(),
name: Cow::Borrowed(source.name()),
class_name: Cow::Borrowed(source.class_name()),
class_name: source.class_name(),
properties,
children: Cow::Borrowed(source.children()),
metadata: Some(InstanceMetadata::from_rojo_metadata(source.metadata())),

View File

@@ -119,7 +119,7 @@ impl UiService {
.map(|(key, value)| {
html! {
<div class="instance-property" title={ Self::display_value(value) }>
{ key.clone() } ": " { format!("{:?}", value.ty()) }
{ key.as_str() } ": " { format!("{:?}", value.ty()) }
</div>
}
})