forked from rojo-rbx/rojo
This doesn't feel ideal. Though it's true that all directories are influenced by any init scripts they have, the directory middleware shouldn't need to know about Lua. I don't really want to go back into working on the middleware chain since it mostly feels like busywork when there are other things to build on in Rojo. also all of this feels really complicated
301 lines
9.1 KiB
Rust
301 lines
9.1 KiB
Rust
use std::{borrow::Cow, str};
|
|
|
|
use maplit::hashmap;
|
|
use rbx_dom_weak::RbxValue;
|
|
|
|
use crate::{
|
|
snapshot::{InstanceMetadata, InstanceSnapshot},
|
|
vfs::{FsResultExt, Vfs, VfsEntry, VfsFetcher},
|
|
};
|
|
|
|
use super::{
|
|
context::InstanceSnapshotContext,
|
|
dir::SnapshotDir,
|
|
meta_file::AdjacentMetadata,
|
|
middleware::{SnapshotInstanceResult, SnapshotMiddleware},
|
|
util::match_trailing,
|
|
};
|
|
|
|
pub struct SnapshotLua;
|
|
|
|
impl SnapshotMiddleware for SnapshotLua {
|
|
fn from_vfs<F: VfsFetcher>(
|
|
context: &mut InstanceSnapshotContext,
|
|
vfs: &Vfs<F>,
|
|
entry: &VfsEntry,
|
|
) -> SnapshotInstanceResult<'static> {
|
|
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
|
|
|
// These paths alter their parent instance, so we don't need to turn
|
|
// them into a script instance here.
|
|
match &*file_name {
|
|
"init.lua" | "init.server.lua" | "init.client.lua" => return Ok(None),
|
|
_ => {}
|
|
}
|
|
|
|
if entry.is_file() {
|
|
snapshot_lua_file(vfs, entry)
|
|
} else {
|
|
// At this point, our entry is definitely a directory!
|
|
|
|
if let Some(snapshot) = snapshot_init(context, vfs, entry, "init.lua")? {
|
|
// An `init.lua` file turns its parent into a ModuleScript
|
|
Ok(Some(snapshot))
|
|
} else if let Some(snapshot) = snapshot_init(context, vfs, entry, "init.server.lua")? {
|
|
// An `init.server.lua` file turns its parent into a Script
|
|
Ok(Some(snapshot))
|
|
} else if let Some(snapshot) = snapshot_init(context, vfs, entry, "init.client.lua")? {
|
|
// An `init.client.lua` file turns its parent into a LocalScript
|
|
Ok(Some(snapshot))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Core routine for turning Lua files into snapshots.
|
|
fn snapshot_lua_file<F: VfsFetcher>(
|
|
vfs: &Vfs<F>,
|
|
entry: &VfsEntry,
|
|
) -> SnapshotInstanceResult<'static> {
|
|
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
|
|
|
let (class_name, instance_name) = if let Some(name) = match_trailing(&file_name, ".server.lua")
|
|
{
|
|
("Script", name)
|
|
} else if let Some(name) = match_trailing(&file_name, ".client.lua") {
|
|
("LocalScript", name)
|
|
} else if let Some(name) = match_trailing(&file_name, ".lua") {
|
|
("ModuleScript", name)
|
|
} else {
|
|
return Ok(None);
|
|
};
|
|
|
|
let contents = entry.contents(vfs)?;
|
|
let contents_str = str::from_utf8(&contents)
|
|
// TODO: Turn into error type
|
|
.expect("File content was not valid UTF-8")
|
|
.to_string();
|
|
|
|
let properties = hashmap! {
|
|
"Source".to_owned() => RbxValue::String {
|
|
value: contents_str,
|
|
},
|
|
};
|
|
|
|
let meta_path = entry
|
|
.path()
|
|
.with_file_name(format!("{}.meta.json", instance_name));
|
|
|
|
let metadata = InstanceMetadata {
|
|
instigating_source: Some(entry.path().to_path_buf().into()),
|
|
relevant_paths: vec![entry.path().to_path_buf(), meta_path.clone()],
|
|
..Default::default()
|
|
};
|
|
|
|
let mut snapshot = InstanceSnapshot {
|
|
snapshot_id: None,
|
|
metadata,
|
|
name: Cow::Owned(instance_name.to_owned()),
|
|
class_name: Cow::Borrowed(class_name),
|
|
properties,
|
|
children: Vec::new(),
|
|
};
|
|
|
|
if let Some(meta_entry) = vfs.get(meta_path).with_not_found()? {
|
|
let meta_contents = meta_entry.contents(vfs)?;
|
|
let mut metadata = AdjacentMetadata::from_slice(&meta_contents);
|
|
metadata.apply_all(&mut snapshot);
|
|
}
|
|
|
|
Ok(Some(snapshot))
|
|
}
|
|
|
|
/// Attempts to snapshot an 'init' Lua script contained inside of a folder with
|
|
/// the given name.
|
|
///
|
|
/// Scripts named `init.lua`, `init.server.lua`, or `init.client.lua` usurp
|
|
/// their parents, which acts similarly to `__init__.py` from the Python world.
|
|
fn snapshot_init<F: VfsFetcher>(
|
|
context: &mut InstanceSnapshotContext,
|
|
vfs: &Vfs<F>,
|
|
folder_entry: &VfsEntry,
|
|
init_name: &str,
|
|
) -> SnapshotInstanceResult<'static> {
|
|
let init_path = folder_entry.path().join(init_name);
|
|
|
|
if let Some(init_entry) = vfs.get(init_path).with_not_found()? {
|
|
if let Some(dir_snapshot) = SnapshotDir::from_vfs(context, vfs, folder_entry)? {
|
|
if let Some(mut init_snapshot) = snapshot_lua_file(vfs, &init_entry)? {
|
|
if dir_snapshot.class_name != "Folder" {
|
|
panic!(
|
|
"init.lua, init.server.lua, and init.client.lua can \
|
|
only be used if the instance produced by the parent \
|
|
directory would be a Folder."
|
|
);
|
|
}
|
|
|
|
init_snapshot.name = dir_snapshot.name;
|
|
init_snapshot.children = dir_snapshot.children;
|
|
init_snapshot.metadata = dir_snapshot.metadata;
|
|
|
|
return Ok(Some(init_snapshot));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
use insta::{assert_yaml_snapshot, with_settings};
|
|
|
|
use crate::vfs::{NoopFetcher, VfsDebug, VfsSnapshot};
|
|
|
|
#[test]
|
|
fn module_from_vfs() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
|
|
vfs.debug_load_snapshot("/foo.lua", file);
|
|
|
|
let entry = vfs.get("/foo.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn server_from_vfs() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
|
|
vfs.debug_load_snapshot("/foo.server.lua", file);
|
|
|
|
let entry = vfs.get("/foo.server.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn client_from_vfs() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
|
|
vfs.debug_load_snapshot("/foo.client.lua", file);
|
|
|
|
let entry = vfs.get("/foo.client.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn init_module_from_vfs() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let dir = VfsSnapshot::dir(hashmap! {
|
|
"init.lua" => VfsSnapshot::file("Hello!"),
|
|
});
|
|
|
|
vfs.debug_load_snapshot("/root", dir);
|
|
|
|
let entry = vfs.get("/root").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn module_with_meta() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
let meta = VfsSnapshot::file(
|
|
r#"
|
|
{
|
|
"ignoreUnknownInstances": true
|
|
}
|
|
"#,
|
|
);
|
|
|
|
vfs.debug_load_snapshot("/foo.lua", file);
|
|
vfs.debug_load_snapshot("/foo.meta.json", meta);
|
|
|
|
let entry = vfs.get("/foo.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn script_with_meta() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
let meta = VfsSnapshot::file(
|
|
r#"
|
|
{
|
|
"ignoreUnknownInstances": true
|
|
}
|
|
"#,
|
|
);
|
|
|
|
vfs.debug_load_snapshot("/foo.server.lua", file);
|
|
vfs.debug_load_snapshot("/foo.meta.json", meta);
|
|
|
|
let entry = vfs.get("/foo.server.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
}
|
|
|
|
#[test]
|
|
fn script_disabled() {
|
|
let mut vfs = Vfs::new(NoopFetcher);
|
|
let file = VfsSnapshot::file("Hello there!");
|
|
let meta = VfsSnapshot::file(
|
|
r#"
|
|
{
|
|
"properties": {
|
|
"Disabled": true
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
|
|
vfs.debug_load_snapshot("/bar.server.lua", file);
|
|
vfs.debug_load_snapshot("/bar.meta.json", meta);
|
|
|
|
let entry = vfs.get("/bar.server.lua").unwrap();
|
|
let instance_snapshot =
|
|
SnapshotLua::from_vfs(&mut InstanceSnapshotContext::default(), &mut vfs, &entry)
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
with_settings!({ sort_maps => true }, {
|
|
assert_yaml_snapshot!(instance_snapshot);
|
|
});
|
|
}
|
|
}
|