Roundtrip schemas in syncback (#1173)

This commit is contained in:
Micah
2025-11-26 16:11:39 -08:00
committed by GitHub
parent a99e877b7c
commit a61a1bef55
64 changed files with 189 additions and 17 deletions

View File

@@ -0,0 +1,10 @@
---
source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)"
---
Writing default.project.json
Writing src/adjacent.luau
Writing src/adjacent.meta.json
Writing src/init/init.meta.json
Writing src/model.model.json
Writing src/init

View File

@@ -0,0 +1,20 @@
---
source: tests/tests/syncback.rs
expression: default.project.json
---
{
"$schema": "rojo/project-schema",
"name": "schema_roundtrip",
"tree": {
"$className": "Folder",
"src": {
"$path": "src"
},
"trigger_reserialization": {
"$className": "BoolValue",
"$properties": {
"Value": true
}
}
}
}

View File

@@ -0,0 +1,10 @@
---
source: tests/tests/syncback.rs
expression: src/adjacent.meta.json
---
{
"$schema": "rojo/adjacent-meta",
"attributes": {
"trigger_reserialization": true
}
}

View File

@@ -0,0 +1,10 @@
---
source: tests/tests/syncback.rs
expression: src/init/init.meta.json
---
{
"$schema": "rojo/init-meta",
"attributes": {
"trigger_reserialization": true
}
}

View File

@@ -0,0 +1,8 @@
---
source: tests/tests/syncback.rs
expression: src/model.model.json
---
{
"$schema": "rojo/model.json",
"className": "BoolValue"
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "rojo/project-schema",
"name": "schema_roundtrip",
"tree": {
"$className": "Folder",
"src": {
"$path": "src"
},
"trigger_reserialization": {
"$className": "BoolValue"
}
}
}

View File

@@ -0,0 +1 @@
-- This file should be ignored.

View File

@@ -0,0 +1,3 @@
{
"$schema": "rojo/adjacent-meta"
}

View File

@@ -0,0 +1,3 @@
{
"$schema": "rojo/init-meta"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "rojo/model.json",
"className": "BoolValue",
"properties": {
"Value": true
}
}

Binary file not shown.

View File

@@ -66,6 +66,10 @@ pub struct InstanceMetadata {
/// The Middleware that was used to create this Instance. Should generally
/// not be `None` except if the snapshotting process is not completed.
pub middleware: Option<Middleware>,
/// A schema provided via a JSON file, if one exists. Will be `None` for
/// all non-JSON middleware.
pub schema: Option<String>,
}
impl InstanceMetadata {
@@ -77,6 +81,7 @@ impl InstanceMetadata {
context: InstanceContext::default(),
specified_id: None,
middleware: None,
schema: None,
}
}
@@ -121,6 +126,10 @@ impl InstanceMetadata {
..self
}
}
pub fn schema(self, schema: Option<String>) -> Self {
Self { schema, ..self }
}
}
impl Default for InstanceMetadata {

View File

@@ -15,5 +15,5 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
children: []

View File

@@ -13,5 +13,5 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
children: []

View File

@@ -15,5 +15,5 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
children: []

View File

@@ -13,5 +13,5 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
children: []

View File

@@ -14,9 +14,9 @@ added_instances:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: New
class_name: Folder
properties: {}
children: []
updated_instances: []

View File

@@ -50,6 +50,7 @@ pub fn snapshot_json_model(
instance.name = Some(name.to_owned());
let id = instance.id.take().map(RojoRef::new);
let schema = instance.schema.take();
let mut snapshot = instance
.into_snapshot()
@@ -60,7 +61,8 @@ pub fn snapshot_json_model(
.instigating_source(path)
.relevant_paths(vec![path.to_path_buf()])
.context(context)
.specified_id(id);
.specified_id(id)
.schema(schema);
Ok(Some(snapshot))
}
@@ -74,6 +76,13 @@ pub fn syncback_json_model<'sync>(
// We don't need the name on the root, but we do for children.
model.name = None;
if let Some(old_inst) = snapshot.old_inst() {
// TODO: Is it worth this being an Arc or Rc? I doubt that enough
// schemas will ever exist in one project for it to matter, but it
// could have a performance cost.
model.schema = old_inst.metadata().schema.clone();
}
Ok(SyncbackReturn {
fs_snapshot: FsSnapshot::new().with_added_file(
&snapshot.path,

View File

@@ -115,6 +115,10 @@ impl AdjacentMetadata {
.map(|inst| inst.metadata().ignore_unknown_instances)
.unwrap_or_default();
let schema = snapshot
.old_inst()
.and_then(|inst| inst.metadata().schema.clone());
let class = &snapshot.new_inst().class;
for (name, value) in snapshot.get_path_filtered_properties(snapshot.new).unwrap() {
match value {
@@ -150,7 +154,7 @@ impl AdjacentMetadata {
attributes,
path,
id: None,
schema: None,
schema,
}))
}
@@ -201,10 +205,19 @@ impl AdjacentMetadata {
Ok(())
}
fn apply_schema(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
if self.schema.is_some() && snapshot.metadata.schema.is_some() {
anyhow::bail!("cannot specify a schema using {} (instance has a schema from somewhere else. how did we get here?)", self.path.display());
}
snapshot.metadata.schema = self.schema.take();
Ok(())
}
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
self.apply_ignore_unknown_instances(snapshot);
self.apply_properties(snapshot)?;
self.apply_id(snapshot)?;
self.apply_schema(snapshot)?;
Ok(())
}
@@ -330,6 +343,10 @@ impl DirectoryMetadata {
.map(|inst| inst.metadata().ignore_unknown_instances)
.unwrap_or_default();
let schema = snapshot
.old_inst()
.and_then(|inst| inst.metadata().schema.clone());
let class = &snapshot.new_inst().class;
for (name, value) in snapshot.get_path_filtered_properties(snapshot.new).unwrap() {
match value {
@@ -366,7 +383,7 @@ impl DirectoryMetadata {
class_name: None,
path,
id: None,
schema: None,
schema,
}))
}
@@ -375,6 +392,7 @@ impl DirectoryMetadata {
self.apply_class_name(snapshot)?;
self.apply_properties(snapshot)?;
self.apply_id(snapshot)?;
self.apply_schema(snapshot)?;
Ok(())
}
@@ -439,6 +457,13 @@ impl DirectoryMetadata {
Ok(())
}
fn apply_schema(&mut self, snapshot: &mut InstanceSnapshot) -> anyhow::Result<()> {
if self.schema.is_some() && snapshot.metadata.schema.is_some() {
anyhow::bail!("cannot specify a schema using {} (instance has a schema from somewhere else. how did we get here?)", self.path.display());
}
snapshot.metadata.schema = self.schema.take();
Ok(())
}
/// Returns whether the metadata is 'empty', meaning it doesn't have anything
/// worth persisting in it. Specifically:
///

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: LocalizationTable
properties:

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: root
class_name: LocalizationTable
properties:

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: root
class_name: LocalizationTable
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: LocalizationTable
properties:

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Folder
properties: {}

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Folder
properties: {}
@@ -46,6 +47,7 @@ children:
emit_legacy_scripts: true
specified_id: ~
middleware: dir
schema: ~
name: Child
class_name: Folder
properties: {}

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -13,6 +13,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: IntValue
properties:
@@ -27,8 +28,8 @@ children:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: The Child
class_name: StringValue
properties: {}
children: []

View File

@@ -13,6 +13,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: IntValue
properties:
@@ -27,8 +28,8 @@ children:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: The Child
class_name: StringValue
properties: {}
children: []

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: LocalScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: bar
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: root
class_name: ModuleScript
properties:

View File

@@ -22,6 +22,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: root
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: bar
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: false
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Script
properties:

View File

@@ -12,6 +12,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: DEFAULT
class_name: DEFAULT
properties: {}

View File

@@ -12,6 +12,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: DEFAULT
class_name: DEFAULT
properties: {}

View File

@@ -12,6 +12,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: DEFAULT
class_name: DEFAULT
properties: {}

View File

@@ -12,6 +12,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: DEFAULT
class_name: DEFAULT
properties: {}

View File

@@ -13,6 +13,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: Model
properties: {}

View File

@@ -13,8 +13,8 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: direct-project
class_name: Model
properties: {}
children: []

View File

@@ -14,10 +14,10 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: project
schema: ~
name: path-property-override
class_name: StringValue
properties:
Value:
String: Changed
children: []

View File

@@ -13,6 +13,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: children
class_name: Folder
properties: {}
@@ -32,8 +33,8 @@ children:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: Child
class_name: Model
properties: {}
children: []

View File

@@ -14,8 +14,8 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: project
schema: ~
name: path-project
class_name: Model
properties: {}
children: []

View File

@@ -14,6 +14,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: project
schema: ~
name: path-child-project
class_name: Folder
properties: {}
@@ -33,8 +34,8 @@ children:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: SomeChild
class_name: Model
properties: {}
children: []

View File

@@ -16,6 +16,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: text
schema: ~
name: path-project
class_name: StringValue
properties:

View File

@@ -13,10 +13,10 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: resolved-properties
class_name: StringValue
properties:
Value:
String: "Hello, world!"
children: []

View File

@@ -13,10 +13,10 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: unresolved-properties
class_name: StringValue
properties:
Value:
String: Hi!
children: []

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: StringValue
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: foo
class_name: StringValue
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: ~
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -15,6 +15,7 @@ metadata:
emit_legacy_scripts: true
specified_id: manually specified
middleware: ~
schema: ~
name: foo
class_name: ModuleScript
properties:

View File

@@ -12,6 +12,9 @@ macro_rules! syncback_tests {
for name in $list {
let snapshot_name = format!(concat!(stringify!($test_name), "-{}"), name);
let new = path.join::<&str>(name);
if !new.exists() {
panic!("the path stub '{}' does not exist after syncback runs. consider double checking for typos.", name);
}
if let Some("rbxm") = new.extension().and_then(OsStr::to_str) {
let content = fs_err::read(new).unwrap();
snapshot_rbxm(&snapshot_name, content, name);
@@ -78,4 +81,5 @@ syncback_tests! {
sync_rules => ["src/module.modulescript", "src/text.text"],
// Ensures that the `syncUnscriptable` setting works
unscriptable_properties => ["default.project.json"],
schema_roundtrip => ["default.project.json", "src/model.model.json", "src/init/init.meta.json", "src/adjacent.meta.json"]
}