Upgrade to rbx_dom_weak 2.0 (#377)

* Mostly mechanical port bits

* Almost there

* It builds again!

* Turn on all the code again

* Tests compiling but not passing

* Stub work for value resolution

* Implement resolution minus enums and derived properties

* Implement property descriptor resolution

* Update referent snapshots

* Update unions test project

Using a place file instead of a model yields better
error messages in Roblox Studio.

* Add easy shortcut to testing with local rbx-dom

* Update rbx-dom

* Add enum resolution

* Update init.meta.json to use UnresolvedValue

* Expand value resolution support, add test

* Filter SharedString values from web API

* Add 'property' builder method to InstanceSnapshot

* Change InstanceSnapshot/InstanceBuilder boundary

* Fix remove_file crash

* rustfmt

* Update to latest rbx_dom_lua

* Update dependencies, including rbx_dom_weak

* Update to latest rbx-dom

* Update dependencies

* Update rbx-dom, fixing more bugs

* Remove experimental warning on binary place builds

* Remove unused imports
This commit is contained in:
Lucien Greathouse
2021-02-18 20:56:09 -05:00
committed by GitHub
parent b84aab0960
commit 59ef5f05ea
63 changed files with 45602 additions and 21004 deletions

View File

@@ -3,7 +3,6 @@ use std::{collections::BTreeMap, path::Path};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
use serde::Serialize;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -30,9 +29,7 @@ pub fn snapshot_csv(
.name(instance_name)
.class_name("LocalizationTable")
.properties(hashmap! {
"Contents".to_owned() => RbxValue::String {
value: table_contents,
},
"Contents".to_owned() => table_contents.into(),
})
.metadata(
InstanceMetadata::new()
@@ -41,8 +38,8 @@ pub fn snapshot_csv(
);
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
metadata.apply_all(&mut snapshot);
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?;
metadata.apply_all(&mut snapshot)?;
}
Ok(Some(snapshot))

View File

@@ -60,8 +60,8 @@ pub fn snapshot_dir(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
);
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
let mut metadata = DirectoryMetadata::from_slice(&meta_contents, &meta_path)?;
metadata.apply_all(&mut snapshot);
let mut metadata = DirectoryMetadata::from_slice(&meta_contents, meta_path)?;
metadata.apply_all(&mut snapshot)?;
}
Ok(Some(snapshot))

View File

@@ -3,7 +3,6 @@ use std::path::Path;
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
use crate::{
lua_ast::{Expression, Statement},
@@ -26,9 +25,7 @@ pub fn snapshot_json(
let as_lua = json_to_lua(value).to_string();
let properties = hashmap! {
"Source".to_owned() => RbxValue::String {
value: as_lua,
},
"Source".to_owned() => as_lua.into(),
};
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
@@ -45,8 +42,8 @@ pub fn snapshot_json(
);
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
metadata.apply_all(&mut snapshot);
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?;
metadata.apply_all(&mut snapshot)?;
}
Ok(Some(snapshot))

View File

@@ -2,11 +2,12 @@ use std::{borrow::Cow, collections::HashMap, path::Path};
use anyhow::Context;
use memofs::Vfs;
use rbx_dom_weak::UnresolvedRbxValue;
use rbx_reflection::try_resolve_value;
use serde::Deserialize;
use crate::snapshot::{InstanceContext, InstanceSnapshot};
use crate::{
resolution::UnresolvedValue,
snapshot::{InstanceContext, InstanceSnapshot},
};
use super::middleware::SnapshotInstanceResult;
@@ -20,7 +21,10 @@ pub fn snapshot_json_model(
let instance: JsonModel = serde_json::from_slice(&contents)
.with_context(|| format!("File is not a valid JSON model: {}", path.display()))?;
let mut snapshot = instance.core.into_snapshot(instance_name.to_owned());
let mut snapshot = instance
.core
.into_snapshot(instance_name.to_owned())
.with_context(|| format!("Could not load JSON model: {}", path.display()))?;
snapshot.metadata = snapshot
.metadata
@@ -58,36 +62,32 @@ struct JsonModelCore {
children: Vec<JsonModelInstance>,
#[serde(default = "HashMap::new", skip_serializing_if = "HashMap::is_empty")]
properties: HashMap<String, UnresolvedRbxValue>,
properties: HashMap<String, UnresolvedValue>,
}
impl JsonModelCore {
fn into_snapshot(self, name: String) -> InstanceSnapshot {
fn into_snapshot(self, name: String) -> anyhow::Result<InstanceSnapshot> {
let class_name = self.class_name;
let children = self
.children
.into_iter()
.map(|child| child.core.into_snapshot(child.name))
.collect();
let mut children = Vec::with_capacity(self.children.len());
for child in self.children {
children.push(child.core.into_snapshot(child.name)?);
}
let properties = self
.properties
.into_iter()
.map(|(key, value)| {
try_resolve_value(&class_name, &key, &value).map(|resolved| (key, resolved))
})
.collect::<Result<HashMap<_, _>, _>>()
.expect("TODO: Handle rbx_reflection errors");
let mut properties = HashMap::with_capacity(self.properties.len());
for (key, unresolved) in self.properties {
let value = unresolved.resolve(&class_name, &key)?;
properties.insert(key, value);
}
InstanceSnapshot {
Ok(InstanceSnapshot {
snapshot_id: None,
metadata: Default::default(),
name: Cow::Owned(name),
class_name: Cow::Owned(class_name),
properties,
children,
}
})
}
}

View File

@@ -3,7 +3,6 @@ use std::{path::Path, str};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -38,9 +37,7 @@ pub fn snapshot_lua(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
.name(instance_name)
.class_name(class_name)
.properties(hashmap! {
"Source".to_owned() => RbxValue::String {
value: contents_str,
},
"Source".to_owned() => contents_str.into(),
})
.metadata(
InstanceMetadata::new()
@@ -50,8 +47,8 @@ pub fn snapshot_lua(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
);
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
metadata.apply_all(&mut snapshot);
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?;
metadata.apply_all(&mut snapshot)?;
}
Ok(Some(snapshot))

View File

@@ -1,11 +1,9 @@
use std::{borrow::Cow, collections::HashMap, path::Path};
use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use anyhow::Context;
use rbx_dom_weak::UnresolvedRbxValue;
use rbx_reflection::try_resolve_value;
use anyhow::{format_err, Context};
use serde::{Deserialize, Serialize};
use crate::snapshot::InstanceSnapshot;
use crate::{resolution::UnresolvedValue, snapshot::InstanceSnapshot};
/// Represents metadata in a sibling file with the same basename.
///
@@ -18,17 +16,23 @@ pub struct AdjacentMetadata {
pub ignore_unknown_instances: Option<bool>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub properties: HashMap<String, UnresolvedRbxValue>,
pub properties: HashMap<String, UnresolvedValue>,
#[serde(skip)]
pub path: PathBuf,
}
impl AdjacentMetadata {
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
serde_json::from_slice(slice).with_context(|| {
pub fn from_slice(slice: &[u8], path: PathBuf) -> anyhow::Result<Self> {
let mut meta: Self = serde_json::from_slice(slice).with_context(|| {
format!(
"File contained malformed .meta.json data: {}",
path.display()
)
})
})?;
meta.path = path;
Ok(meta)
}
pub fn apply_ignore_unknown_instances(&mut self, snapshot: &mut InstanceSnapshot) {
@@ -37,23 +41,24 @@ impl AdjacentMetadata {
}
}
pub fn apply_properties(&mut self, snapshot: &mut InstanceSnapshot) {
let class_name = &snapshot.class_name;
pub fn apply_properties(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
let path = &self.path;
let source_properties = self.properties.drain().map(|(key, value)| {
try_resolve_value(class_name, &key, &value)
.map(|resolved| (key, resolved))
.expect("TODO: Handle rbx_reflection errors")
});
for (key, unresolved) in self.properties.drain() {
let value = unresolved
.resolve(&snapshot.class_name, &key)
.with_context(|| format!("error applying meta file {}", path.display()))?;
for (key, value) in source_properties {
snapshot.properties.insert(key, value);
}
Ok(())
}
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) {
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
self.apply_ignore_unknown_instances(snapshot);
self.apply_properties(snapshot);
self.apply_properties(snapshot)?;
Ok(())
}
// TODO: Add method to allow selectively applying parts of metadata and
@@ -71,37 +76,50 @@ pub struct DirectoryMetadata {
pub ignore_unknown_instances: Option<bool>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub properties: HashMap<String, UnresolvedRbxValue>,
pub properties: HashMap<String, UnresolvedValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub class_name: Option<String>,
#[serde(skip)]
pub path: PathBuf,
}
impl DirectoryMetadata {
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
serde_json::from_slice(slice).with_context(|| {
pub fn from_slice(slice: &[u8], path: PathBuf) -> anyhow::Result<Self> {
let mut meta: Self = serde_json::from_slice(slice).with_context(|| {
format!(
"File contained malformed init.meta.json data: {}",
path.display()
)
})
})?;
meta.path = path;
Ok(meta)
}
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) {
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
self.apply_ignore_unknown_instances(snapshot);
self.apply_class_name(snapshot);
self.apply_properties(snapshot);
self.apply_class_name(snapshot)?;
self.apply_properties(snapshot)?;
Ok(())
}
fn apply_class_name(&mut self, snapshot: &mut InstanceSnapshot) {
fn apply_class_name(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
if let Some(class_name) = self.class_name.take() {
if snapshot.class_name != "Folder" {
// TODO: Turn into error type
panic!("className in init.meta.json can only be specified if the affected directory would turn into a Folder instance.");
return Err(format_err!(
"className in init.meta.json can only be specified if the \
affected directory would turn into a Folder instance."
));
}
snapshot.class_name = Cow::Owned(class_name);
}
Ok(())
}
fn apply_ignore_unknown_instances(&mut self, snapshot: &mut InstanceSnapshot) {
@@ -110,17 +128,17 @@ impl DirectoryMetadata {
}
}
fn apply_properties(&mut self, snapshot: &mut InstanceSnapshot) {
let class_name = &snapshot.class_name;
fn apply_properties(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
let path = &self.path;
let source_properties = self.properties.drain().map(|(key, value)| {
try_resolve_value(class_name, &key, &value)
.map(|resolved| (key, resolved))
.expect("TODO: Handle rbx_reflection errors")
});
for (key, unresolved) in self.properties.drain() {
let value = unresolved
.resolve(&snapshot.class_name, &key)
.with_context(|| format!("error applying meta file {}", path.display()))?;
for (key, value) in source_properties {
snapshot.properties.insert(key, value);
}
Ok(())
}
}

View File

@@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::HashMap, path::Path};
use anyhow::Context;
use memofs::Vfs;
use rbx_reflection::{get_class_descriptor, try_resolve_value};
use rbx_reflection::ClassTag;
use crate::{
project::{Project, ProjectNode},
@@ -140,9 +140,9 @@ pub fn snapshot_project_node(
// Members of DataModel with names that match known services are
// probably supposed to be those services.
let descriptor = get_class_descriptor(&name)?;
let descriptor = rbx_reflection_database::get().classes.get(&name)?;
if descriptor.is_service() {
if descriptor.tags.contains(&ClassTag::Service) {
return Some(name.clone());
}
} else if parent_class == "StarterPlayer" {
@@ -171,11 +171,18 @@ pub fn snapshot_project_node(
}
}
for (key, value) in &node.properties {
let resolved_value = try_resolve_value(&class_name, key, value)
.expect("TODO: Properly handle value resolution errors");
for (key, unresolved) in &node.properties {
let value = unresolved
.clone()
.resolve(&class_name, key)
.with_context(|| {
format!(
"Unresolvable property in project at path {}",
project_path.display()
)
})?;
properties.insert(key.clone(), resolved_value);
properties.insert(key.clone(), value);
}
// If the user specified $ignoreUnknownInstances, overwrite the existing

View File

@@ -1,8 +1,7 @@
use std::{collections::HashMap, path::Path};
use std::path::Path;
use anyhow::Context;
use memofs::Vfs;
use rbx_dom_weak::{RbxInstanceProperties, RbxTree};
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -14,18 +13,11 @@ pub fn snapshot_rbxm(
path: &Path,
instance_name: &str,
) -> SnapshotInstanceResult {
let mut temp_tree = RbxTree::new(RbxInstanceProperties {
name: "DataModel".to_owned(),
class_name: "DataModel".to_owned(),
properties: HashMap::new(),
});
let root_id = temp_tree.get_root_id();
rbx_binary::decode(&mut temp_tree, root_id, vfs.read(path)?.as_slice())
let temp_tree = rbx_binary::from_reader_default(vfs.read(path)?.as_slice())
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
let root_instance = temp_tree.get_instance(root_id).unwrap();
let children = root_instance.get_children_ids();
let root_instance = temp_tree.root();
let children = root_instance.children();
if children.len() == 1 {
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])

View File

@@ -19,8 +19,8 @@ pub fn snapshot_rbxmx(
let temp_tree = rbx_xml::from_reader(vfs.read(path)?.as_slice(), options)
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
let root_instance = temp_tree.get_instance(temp_tree.get_root_id()).unwrap();
let children = root_instance.get_children_ids();
let root_instance = temp_tree.root();
let children = root_instance.children();
if children.len() == 1 {
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])

View File

@@ -14,7 +14,7 @@ name: foo
class_name: IntValue
properties:
Value:
Type: Int32
Type: Int64
Value: 5
children:
- snapshot_id: ~

View File

@@ -3,7 +3,6 @@ use std::{path::Path, str};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -21,9 +20,7 @@ pub fn snapshot_txt(
.to_owned();
let properties = hashmap! {
"Value".to_owned() => RbxValue::String {
value: contents_str,
},
"Value".to_owned() => contents_str.into(),
};
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
@@ -40,8 +37,8 @@ pub fn snapshot_txt(
);
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
metadata.apply_all(&mut snapshot);
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?;
metadata.apply_all(&mut snapshot)?;
}
Ok(Some(snapshot))