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

@@ -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>
}
})