mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-22 13:45:16 +00:00
Implement Syncback to support converting Roblox files to a Rojo project (#937)
This is a very large commit. Consider checking the linked PR for more information.
This commit is contained in:
@@ -1,11 +1,22 @@
|
||||
use std::{path::Path, str};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use memofs::Vfs;
|
||||
use rbx_dom_weak::{types::Enum, ustr, HashMapExt as _, UstrMap};
|
||||
use rbx_dom_weak::{
|
||||
types::{Enum, Variant},
|
||||
ustr, HashMapExt as _, UstrMap,
|
||||
};
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||
use crate::{
|
||||
snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot},
|
||||
syncback::{FsSnapshot, SyncbackReturn, SyncbackSnapshot},
|
||||
};
|
||||
|
||||
use super::{dir::snapshot_dir_no_meta, meta_file::AdjacentMetadata, meta_file::DirectoryMetadata};
|
||||
use super::{
|
||||
dir::{snapshot_dir_no_meta, syncback_dir_no_meta},
|
||||
meta_file::{AdjacentMetadata, DirectoryMetadata},
|
||||
PathExt as _,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScriptType {
|
||||
@@ -95,10 +106,11 @@ pub fn snapshot_lua_init(
|
||||
context: &InstanceContext,
|
||||
vfs: &Vfs,
|
||||
init_path: &Path,
|
||||
name: &str,
|
||||
script_type: ScriptType,
|
||||
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||
let folder_path = init_path.parent().unwrap();
|
||||
let dir_snapshot = snapshot_dir_no_meta(context, vfs, folder_path)?.unwrap();
|
||||
let dir_snapshot = snapshot_dir_no_meta(context, vfs, folder_path, name)?.unwrap();
|
||||
|
||||
if dir_snapshot.class_name != "Folder" {
|
||||
anyhow::bail!(
|
||||
@@ -117,12 +129,89 @@ pub fn snapshot_lua_init(
|
||||
|
||||
init_snapshot.children = dir_snapshot.children;
|
||||
init_snapshot.metadata = dir_snapshot.metadata;
|
||||
// The directory snapshot middleware includes all possible init paths
|
||||
// so we don't need to add it here.
|
||||
|
||||
DirectoryMetadata::read_and_apply_all(vfs, folder_path, &mut init_snapshot)?;
|
||||
|
||||
Ok(Some(init_snapshot))
|
||||
}
|
||||
|
||||
pub fn syncback_lua<'sync>(
|
||||
snapshot: &SyncbackSnapshot<'sync>,
|
||||
) -> anyhow::Result<SyncbackReturn<'sync>> {
|
||||
let new_inst = snapshot.new_inst();
|
||||
|
||||
let contents = if let Some(Variant::String(source)) = new_inst.properties.get(&ustr("Source")) {
|
||||
source.as_bytes().to_vec()
|
||||
} else {
|
||||
anyhow::bail!("Scripts must have a `Source` property that is a String")
|
||||
};
|
||||
let mut fs_snapshot = FsSnapshot::new();
|
||||
fs_snapshot.add_file(&snapshot.path, contents);
|
||||
|
||||
let meta = AdjacentMetadata::from_syncback_snapshot(snapshot, snapshot.path.clone())?;
|
||||
if let Some(mut meta) = meta {
|
||||
// Scripts have relatively few properties that we care about, so shifting
|
||||
// is fine.
|
||||
meta.properties.shift_remove(&ustr("Source"));
|
||||
|
||||
if !meta.is_empty() {
|
||||
let parent_location = snapshot.path.parent_err()?;
|
||||
fs_snapshot.add_file(
|
||||
parent_location.join(format!("{}.meta.json", new_inst.name)),
|
||||
serde_json::to_vec_pretty(&meta).context("cannot serialize metadata")?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SyncbackReturn {
|
||||
fs_snapshot,
|
||||
// Scripts don't have a child!
|
||||
children: Vec::new(),
|
||||
removed_children: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn syncback_lua_init<'sync>(
|
||||
script_type: ScriptType,
|
||||
snapshot: &SyncbackSnapshot<'sync>,
|
||||
) -> anyhow::Result<SyncbackReturn<'sync>> {
|
||||
let new_inst = snapshot.new_inst();
|
||||
let path = snapshot.path.join(match script_type {
|
||||
ScriptType::Server => "init.server.luau",
|
||||
ScriptType::Client => "init.client.luau",
|
||||
ScriptType::Module => "init.luau",
|
||||
_ => anyhow::bail!("syncback is not yet implemented for {script_type:?}"),
|
||||
});
|
||||
|
||||
let contents = if let Some(Variant::String(source)) = new_inst.properties.get(&ustr("Source")) {
|
||||
source.as_bytes().to_vec()
|
||||
} else {
|
||||
anyhow::bail!("Scripts must have a `Source` property that is a String")
|
||||
};
|
||||
|
||||
let mut dir_syncback = syncback_dir_no_meta(snapshot)?;
|
||||
dir_syncback.fs_snapshot.add_file(&path, contents);
|
||||
|
||||
let meta = DirectoryMetadata::from_syncback_snapshot(snapshot, path.clone())?;
|
||||
if let Some(mut meta) = meta {
|
||||
// Scripts have relatively few properties that we care about, so shifting
|
||||
// is fine.
|
||||
meta.properties.shift_remove(&ustr("Source"));
|
||||
|
||||
if !meta.is_empty() {
|
||||
dir_syncback.fs_snapshot.add_file(
|
||||
snapshot.path.join("init.meta.json"),
|
||||
serde_json::to_vec_pretty(&meta)
|
||||
.context("could not serialize new init.meta.json")?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dir_syncback)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -305,6 +394,7 @@ mod test {
|
||||
&InstanceContext::with_emit_legacy_scripts(Some(true)),
|
||||
&vfs,
|
||||
Path::new("/root/init.lua"),
|
||||
"root",
|
||||
ScriptType::Module,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -336,6 +426,7 @@ mod test {
|
||||
&InstanceContext::with_emit_legacy_scripts(Some(true)),
|
||||
&vfs,
|
||||
Path::new("/root/init.lua"),
|
||||
"root",
|
||||
ScriptType::Module,
|
||||
)
|
||||
.unwrap()
|
||||
|
||||
Reference in New Issue
Block a user