forked from rojo-rbx/rojo
rustfmt the codebase
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
process::Command,
|
||||
};
|
||||
use std::{fs, path::Path, process::Command};
|
||||
|
||||
use insta::assert_snapshot_matches;
|
||||
use tempfile::tempdir;
|
||||
@@ -67,7 +63,10 @@ fn run_build_test(test_name: &str) {
|
||||
|
||||
let status = Command::new(exe_path)
|
||||
.args(&[
|
||||
"build", input_path.to_str().unwrap(), "-o", output_path.to_str().unwrap(),
|
||||
"build",
|
||||
input_path.to_str().unwrap(),
|
||||
"-o",
|
||||
output_path.to_str().unwrap(),
|
||||
])
|
||||
.env("RUST_LOG", "error")
|
||||
.current_dir(working_dir)
|
||||
@@ -76,8 +75,7 @@ fn run_build_test(test_name: &str) {
|
||||
|
||||
assert!(status.success(), "Rojo did not exit successfully");
|
||||
|
||||
let contents = fs::read_to_string(&output_path)
|
||||
.expect("Couldn't read output file");
|
||||
let contents = fs::read_to_string(&output_path).expect("Couldn't read output file");
|
||||
|
||||
assert_snapshot_matches!(test_name, contents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#[cfg(test)]
|
||||
mod build_test;
|
||||
mod build_test;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
reorder_imports = true
|
||||
reorder_imported_names = true
|
||||
reorder_imports_in_group = true
|
||||
attributes_on_same_line_as_field = false
|
||||
attributes_on_same_line_as_variant = false
|
||||
chain_split_single_child = true
|
||||
wrap_comments = true
|
||||
imports_indent = "Block"
|
||||
match_block_trailing_comma = true
|
||||
match_pattern_separator_break_point = "Front"
|
||||
error_on_line_overflow = false
|
||||
struct_lit_multiline_style = "ForceMulti"
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::{
|
||||
env,
|
||||
panic,
|
||||
env, panic,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use clap::{clap_app, ArgMatches};
|
||||
use log::error;
|
||||
|
||||
use librojo::commands;
|
||||
|
||||
@@ -21,8 +20,7 @@ fn make_path_absolute(value: &Path) -> PathBuf {
|
||||
|
||||
fn main() {
|
||||
{
|
||||
let log_env = env_logger::Env::default()
|
||||
.default_filter_or("warn");
|
||||
let log_env = env_logger::Env::default().default_filter_or("warn");
|
||||
|
||||
env_logger::Builder::from_env(log_env)
|
||||
.default_format_timestamp(false)
|
||||
@@ -95,7 +93,8 @@ fn show_crash_message(message: &str) {
|
||||
}
|
||||
|
||||
fn start_init(sub_matches: &ArgMatches) {
|
||||
let fuzzy_project_path = make_path_absolute(Path::new(sub_matches.value_of("PATH").unwrap_or("")));
|
||||
let fuzzy_project_path =
|
||||
make_path_absolute(Path::new(sub_matches.value_of("PATH").unwrap_or("")));
|
||||
let kind = sub_matches.value_of("kind");
|
||||
|
||||
let options = commands::InitOptions {
|
||||
@@ -104,11 +103,11 @@ fn start_init(sub_matches: &ArgMatches) {
|
||||
};
|
||||
|
||||
match commands::init(&options) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +123,7 @@ fn start_serve(sub_matches: &ArgMatches) {
|
||||
Err(_) => {
|
||||
error!("Invalid port {}", v);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
@@ -135,11 +134,11 @@ fn start_serve(sub_matches: &ArgMatches) {
|
||||
};
|
||||
|
||||
match commands::serve(&options) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,11 +157,11 @@ fn start_build(sub_matches: &ArgMatches) {
|
||||
};
|
||||
|
||||
match commands::build(&options) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ fn start_upload(sub_matches: &ArgMatches) {
|
||||
Err(_) => {
|
||||
error!("Invalid place ID {}", arg);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,10 +194,10 @@ fn start_upload(sub_matches: &ArgMatches) {
|
||||
};
|
||||
|
||||
match commands::upload(&options) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
io::{self, Write, BufWriter},
|
||||
io::{self, BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxInstanceProperties};
|
||||
use failure::Fail;
|
||||
use rbx_dom_weak::{RbxInstanceProperties, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, RealFetcher, WatchMode, FsError},
|
||||
imfs::new::{FsError, Imfs, RealFetcher, WatchMode},
|
||||
snapshot::{apply_patch_set, compute_patch_set},
|
||||
snapshot_middleware::snapshot_from_imfs,
|
||||
};
|
||||
@@ -67,12 +67,12 @@ impl_from!(BuildError {
|
||||
});
|
||||
|
||||
fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
||||
rbx_xml::EncodeOptions::new()
|
||||
.property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||
}
|
||||
|
||||
pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
let output_kind = options.output_kind
|
||||
let output_kind = options
|
||||
.output_kind
|
||||
.or_else(|| detect_output_kind(options))
|
||||
.ok_or(BuildError::UnknownOutputKind)?;
|
||||
|
||||
@@ -89,7 +89,8 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
let mut imfs = Imfs::new(RealFetcher::new(WatchMode::Disabled));
|
||||
|
||||
log::trace!("Reading project root");
|
||||
let entry = imfs.get(&options.fuzzy_project_path)
|
||||
let entry = imfs
|
||||
.get(&options.fuzzy_project_path)
|
||||
.expect("could not get project path");
|
||||
|
||||
log::trace!("Generating snapshot of instances from IMFS");
|
||||
@@ -112,17 +113,17 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
// descendants.
|
||||
|
||||
rbx_xml::to_writer(&mut file, &tree, &[root_id], xml_encode_config())?;
|
||||
},
|
||||
}
|
||||
OutputKind::Rbxlx => {
|
||||
// Place files don't contain an entry for the DataModel, but our
|
||||
// RbxTree representation does.
|
||||
|
||||
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
|
||||
rbx_xml::to_writer(&mut file, &tree, top_level_ids, xml_encode_config())?;
|
||||
},
|
||||
}
|
||||
OutputKind::Rbxm => {
|
||||
rbx_binary::encode(&tree, &[root_id], &mut file)?;
|
||||
},
|
||||
}
|
||||
OutputKind::Rbxl => {
|
||||
log::warn!("Support for building binary places (rbxl) is still experimental.");
|
||||
log::warn!("Using the XML place format (rbxlx) is recommended instead.");
|
||||
@@ -130,7 +131,7 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
|
||||
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
|
||||
rbx_binary::encode(&tree, top_level_ids, &mut file)?;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
@@ -138,4 +139,4 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
log::trace!("Done!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
@@ -8,11 +6,14 @@ use crate::project::{Project, ProjectInitError};
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum InitError {
|
||||
#[fail(display = "Invalid project kind '{}', valid kinds are 'place' and 'model'", _0)]
|
||||
#[fail(
|
||||
display = "Invalid project kind '{}', valid kinds are 'place' and 'model'",
|
||||
_0
|
||||
)]
|
||||
InvalidKind(String),
|
||||
|
||||
#[fail(display = "Project init error: {}", _0)]
|
||||
ProjectInitError(#[fail(cause)] ProjectInitError)
|
||||
ProjectInitError(#[fail(cause)] ProjectInitError),
|
||||
}
|
||||
|
||||
impl_from!(InitError {
|
||||
@@ -30,15 +31,19 @@ pub fn init(options: &InitOptions) -> Result<(), InitError> {
|
||||
Some("place") | None => {
|
||||
let path = Project::init_place(&options.fuzzy_project_path)?;
|
||||
(path, "place")
|
||||
},
|
||||
}
|
||||
Some("model") => {
|
||||
let path = Project::init_model(&options.fuzzy_project_path)?;
|
||||
(path, "model")
|
||||
},
|
||||
}
|
||||
Some(invalid) => return Err(InitError::InvalidKind(invalid.to_string())),
|
||||
};
|
||||
|
||||
println!("Created new {} project file at {}", project_kind, project_path.display());
|
||||
println!(
|
||||
"Created new {} project file at {}",
|
||||
project_kind,
|
||||
project_path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
mod serve;
|
||||
mod init;
|
||||
mod build;
|
||||
mod init;
|
||||
mod serve;
|
||||
mod upload;
|
||||
|
||||
pub use self::serve::*;
|
||||
pub use self::init::*;
|
||||
pub use self::build::*;
|
||||
pub use self::upload::*;
|
||||
pub use self::init::*;
|
||||
pub use self::serve::*;
|
||||
pub use self::upload::*;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxInstanceProperties};
|
||||
use failure::Fail;
|
||||
use rbx_dom_weak::{RbxInstanceProperties, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, RealFetcher, WatchMode},
|
||||
@@ -41,8 +37,11 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
Err(other) => return Err(other.into()),
|
||||
};
|
||||
|
||||
let port = options.port
|
||||
.or(maybe_project.as_ref().and_then(|project| project.serve_port))
|
||||
let port = options
|
||||
.port
|
||||
.or(maybe_project
|
||||
.as_ref()
|
||||
.and_then(|project| project.serve_port))
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
|
||||
println!("Rojo server listening on port {}", port);
|
||||
@@ -55,7 +54,8 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
let root_id = tree.get_root_id();
|
||||
|
||||
let mut imfs = Imfs::new(RealFetcher::new(WatchMode::Enabled));
|
||||
let entry = imfs.get(&options.fuzzy_project_path)
|
||||
let entry = imfs
|
||||
.get(&options.fuzzy_project_path)
|
||||
.expect("could not get project path");
|
||||
|
||||
let snapshot = snapshot_from_imfs(&mut imfs, &entry)
|
||||
@@ -86,4 +86,4 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ pub struct UploadOptions<'a> {
|
||||
|
||||
pub fn upload(_options: &UploadOptions) -> Result<(), UploadError> {
|
||||
unimplemented!("TODO: Reimplement upload command");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use std::{
|
||||
io,
|
||||
fmt,
|
||||
path::PathBuf,
|
||||
};
|
||||
use std::{fmt, io, path::PathBuf};
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
@@ -62,4 +58,4 @@ impl fmt::Display for FsError {
|
||||
fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(output, "{}: {}", self.path.display(), self.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ pub trait ImfsFetcher {
|
||||
fn watch(&mut self, path: &Path);
|
||||
fn unwatch(&mut self, path: &Path);
|
||||
fn receiver(&self) -> Receiver<ImfsEvent>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ use crossbeam_channel::Receiver;
|
||||
use crate::path_map::PathMap;
|
||||
|
||||
use super::{
|
||||
error::{FsError, FsResult},
|
||||
fetcher::{FileType, ImfsEvent, ImfsFetcher},
|
||||
snapshot::ImfsSnapshot,
|
||||
error::{FsResult, FsError},
|
||||
fetcher::{ImfsFetcher, FileType, ImfsEvent},
|
||||
};
|
||||
|
||||
/// An in-memory filesystem that can be incrementally populated and updated as
|
||||
@@ -89,16 +89,22 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
|
||||
match snapshot {
|
||||
ImfsSnapshot::File(file) => {
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::File(ImfsFile {
|
||||
path: path.to_path_buf(),
|
||||
contents: Some(file.contents),
|
||||
}));
|
||||
self.inner.insert(
|
||||
path.to_path_buf(),
|
||||
ImfsItem::File(ImfsFile {
|
||||
path: path.to_path_buf(),
|
||||
contents: Some(file.contents),
|
||||
}),
|
||||
);
|
||||
}
|
||||
ImfsSnapshot::Directory(directory) => {
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::Directory(ImfsDirectory {
|
||||
path: path.to_path_buf(),
|
||||
children_enumerated: true,
|
||||
}));
|
||||
self.inner.insert(
|
||||
path.to_path_buf(),
|
||||
ImfsItem::Directory(ImfsDirectory {
|
||||
path: path.to_path_buf(),
|
||||
children_enumerated: true,
|
||||
}),
|
||||
);
|
||||
|
||||
for (child_name, child) in directory.children.into_iter() {
|
||||
self.load_from_snapshot(path.join(child_name), child);
|
||||
@@ -114,7 +120,9 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_type = self.fetcher.file_type(path)
|
||||
let new_type = self
|
||||
.fetcher
|
||||
.file_type(path)
|
||||
.map_err(|err| FsError::new(err, path.to_path_buf()))?;
|
||||
|
||||
match self.inner.get_mut(path) {
|
||||
@@ -131,18 +139,25 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
}
|
||||
(ImfsItem::File(_), FileType::Directory) => {
|
||||
self.inner.remove(path);
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::new_from_type(FileType::Directory, path));
|
||||
self.inner.insert(
|
||||
path.to_path_buf(),
|
||||
ImfsItem::new_from_type(FileType::Directory, path),
|
||||
);
|
||||
self.fetcher.watch(path);
|
||||
}
|
||||
(ImfsItem::Directory(_), FileType::File) => {
|
||||
self.inner.remove(path);
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::new_from_type(FileType::File, path));
|
||||
self.inner.insert(
|
||||
path.to_path_buf(),
|
||||
ImfsItem::new_from_type(FileType::File, path),
|
||||
);
|
||||
self.fetcher.unwatch(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::new_from_type(new_type, path));
|
||||
self.inner
|
||||
.insert(path.to_path_buf(), ImfsItem::new_from_type(new_type, path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,13 +200,19 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
match self.inner.get_mut(path).unwrap() {
|
||||
ImfsItem::File(file) => {
|
||||
if file.contents.is_none() {
|
||||
file.contents = Some(self.fetcher.read_contents(path)
|
||||
.map_err(|err| FsError::new(err, path.to_path_buf()))?);
|
||||
file.contents = Some(
|
||||
self.fetcher
|
||||
.read_contents(path)
|
||||
.map_err(|err| FsError::new(err, path.to_path_buf()))?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(file.contents.as_ref().unwrap())
|
||||
}
|
||||
ImfsItem::Directory(_) => Err(FsError::new(io::Error::new(io::ErrorKind::Other, "Can't read a directory"), path.to_path_buf()))
|
||||
ImfsItem::Directory(_) => Err(FsError::new(
|
||||
io::Error::new(io::ErrorKind::Other, "Can't read a directory"),
|
||||
path.to_path_buf(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +226,9 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
self.fetcher.watch(path);
|
||||
|
||||
if dir.children_enumerated {
|
||||
return self.inner.children(path)
|
||||
return self
|
||||
.inner
|
||||
.children(path)
|
||||
.unwrap() // TODO: Handle None here, which means the PathMap entry did not exist.
|
||||
.into_iter()
|
||||
.map(PathBuf::from) // Convert paths from &Path to PathBuf
|
||||
@@ -215,13 +238,17 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
.collect::<FsResult<Vec<ImfsEntry>>>();
|
||||
}
|
||||
|
||||
self.fetcher.read_children(path)
|
||||
self.fetcher
|
||||
.read_children(path)
|
||||
.map_err(|err| FsError::new(err, path.to_path_buf()))?
|
||||
.into_iter()
|
||||
.map(|path| self.get(path))
|
||||
.collect::<FsResult<Vec<ImfsEntry>>>()
|
||||
}
|
||||
ImfsItem::File(_) => Err(FsError::new(io::Error::new(io::ErrorKind::Other, "Can't read a directory"), path.to_path_buf()))
|
||||
ImfsItem::File(_) => Err(FsError::new(
|
||||
io::Error::new(io::ErrorKind::Other, "Can't read a directory"),
|
||||
path.to_path_buf(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,14 +283,17 @@ impl<F: ImfsFetcher> Imfs<F> {
|
||||
/// is using, this call may read exactly only the given path and no more.
|
||||
fn read_if_not_exists(&mut self, path: &Path) -> FsResult<()> {
|
||||
if !self.inner.contains_key(path) {
|
||||
let kind = self.fetcher.file_type(path)
|
||||
let kind = self
|
||||
.fetcher
|
||||
.file_type(path)
|
||||
.map_err(|err| FsError::new(err, path.to_path_buf()))?;
|
||||
|
||||
if kind == FileType::Directory {
|
||||
self.fetcher.watch(path);
|
||||
}
|
||||
|
||||
self.inner.insert(path.to_path_buf(), ImfsItem::new_from_type(kind, path));
|
||||
self.inner
|
||||
.insert(path.to_path_buf(), ImfsItem::new_from_type(kind, path));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -293,10 +323,7 @@ impl ImfsEntry {
|
||||
imfs.get_contents(&self.path)
|
||||
}
|
||||
|
||||
pub fn children(
|
||||
&self,
|
||||
imfs: &mut Imfs<impl ImfsFetcher>,
|
||||
) -> FsResult<Vec<ImfsEntry>> {
|
||||
pub fn children(&self, imfs: &mut Imfs<impl ImfsFetcher>) -> FsResult<Vec<ImfsEntry>> {
|
||||
imfs.get_children(&self.path)
|
||||
}
|
||||
|
||||
@@ -352,19 +379,12 @@ pub struct ImfsDirectory {
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::{
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
use maplit::hashmap;
|
||||
|
||||
use super::super::{
|
||||
noop_fetcher::NoopFetcher,
|
||||
error::FsErrorKind,
|
||||
fetcher::ImfsEvent,
|
||||
};
|
||||
use super::super::{error::FsErrorKind, fetcher::ImfsEvent, noop_fetcher::NoopFetcher};
|
||||
|
||||
#[test]
|
||||
fn from_snapshot_file() {
|
||||
@@ -458,11 +478,9 @@ mod test {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn watch(&mut self, _path: &Path) {
|
||||
}
|
||||
fn watch(&mut self, _path: &Path) {}
|
||||
|
||||
fn unwatch(&mut self, _path: &Path) {
|
||||
}
|
||||
fn unwatch(&mut self, _path: &Path) {}
|
||||
|
||||
fn receiver(&self) -> Receiver<ImfsEvent> {
|
||||
crossbeam_channel::never()
|
||||
@@ -477,11 +495,9 @@ mod test {
|
||||
inner: mock_state.clone(),
|
||||
});
|
||||
|
||||
let a = imfs.get("/dir/a.txt")
|
||||
.expect("mock file did not exist");
|
||||
let a = imfs.get("/dir/a.txt").expect("mock file did not exist");
|
||||
|
||||
let contents = a.contents(&mut imfs)
|
||||
.expect("mock file contents error");
|
||||
let contents = a.contents(&mut imfs).expect("mock file contents error");
|
||||
|
||||
assert_eq!(contents, b"Initial contents");
|
||||
|
||||
@@ -493,8 +509,7 @@ mod test {
|
||||
imfs.raise_file_changed("/dir/a.txt")
|
||||
.expect("error processing file change");
|
||||
|
||||
let contents = a.contents(&mut imfs)
|
||||
.expect("mock file contents error");
|
||||
let contents = a.contents(&mut imfs).expect("mock file contents error");
|
||||
|
||||
assert_eq!(contents, b"Changed contents");
|
||||
}
|
||||
@@ -506,10 +521,10 @@ mod test {
|
||||
let file = ImfsSnapshot::file("hello, world!");
|
||||
imfs.load_from_snapshot("/hello.txt", file);
|
||||
|
||||
let hello = imfs.get("/hello.txt")
|
||||
.expect("couldn't get hello.txt");
|
||||
let hello = imfs.get("/hello.txt").expect("couldn't get hello.txt");
|
||||
|
||||
let contents = hello.contents(&mut imfs)
|
||||
let contents = hello
|
||||
.contents(&mut imfs)
|
||||
.expect("couldn't get hello.txt contents");
|
||||
|
||||
assert_eq!(contents, b"hello, world!");
|
||||
@@ -527,4 +542,4 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ pub use error::*;
|
||||
|
||||
pub mod new {
|
||||
pub use super::error::*;
|
||||
pub use super::imfs::*;
|
||||
pub use super::fetcher::*;
|
||||
pub use super::real_fetcher::*;
|
||||
pub use super::imfs::*;
|
||||
pub use super::noop_fetcher::*;
|
||||
pub use super::real_fetcher::*;
|
||||
pub use super::snapshot::*;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,30 @@ use std::{
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
|
||||
use super::fetcher::{ImfsFetcher, FileType, ImfsEvent};
|
||||
use super::fetcher::{FileType, ImfsEvent, ImfsFetcher};
|
||||
|
||||
pub struct NoopFetcher;
|
||||
|
||||
impl ImfsFetcher for NoopFetcher {
|
||||
fn file_type(&mut self, _path: &Path) -> io::Result<FileType> {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "NoopFetcher always returns NotFound"))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"NoopFetcher always returns NotFound",
|
||||
))
|
||||
}
|
||||
|
||||
fn read_children(&mut self, _path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "NoopFetcher always returns NotFound"))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"NoopFetcher always returns NotFound",
|
||||
))
|
||||
}
|
||||
|
||||
fn read_contents(&mut self, _path: &Path) -> io::Result<Vec<u8>> {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "NoopFetcher always returns NotFound"))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"NoopFetcher always returns NotFound",
|
||||
))
|
||||
}
|
||||
|
||||
fn create_directory(&mut self, _path: &Path) -> io::Result<()> {
|
||||
@@ -37,13 +46,11 @@ impl ImfsFetcher for NoopFetcher {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watch(&mut self, _path: &Path) {
|
||||
}
|
||||
fn watch(&mut self, _path: &Path) {}
|
||||
|
||||
fn unwatch(&mut self, _path: &Path) {
|
||||
}
|
||||
fn unwatch(&mut self, _path: &Path) {}
|
||||
|
||||
fn receiver(&self) -> Receiver<ImfsEvent> {
|
||||
crossbeam_channel::never()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
//! std::fs interface and notify as the file watcher.
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
io,
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crossbeam_channel::{unbounded, Receiver};
|
||||
use jod_thread::JoinHandle;
|
||||
use crossbeam_channel::{Receiver, unbounded};
|
||||
use notify::{RecursiveMode, RecommendedWatcher, Watcher};
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
use super::fetcher::{ImfsFetcher, FileType, ImfsEvent};
|
||||
use super::fetcher::{FileType, ImfsEvent, ImfsFetcher};
|
||||
|
||||
/// Workaround to disable the file watcher for processes that don't need it,
|
||||
/// since notify appears hang on to mpsc Sender objects too long, causing Rojo
|
||||
@@ -51,7 +50,7 @@ impl RealFetcher {
|
||||
.spawn(move || {
|
||||
notify_receiver
|
||||
.into_iter()
|
||||
.for_each(|event| { sender.send(event).unwrap() });
|
||||
.for_each(|event| sender.send(event).unwrap());
|
||||
})
|
||||
.expect("Could not start message converter thread");
|
||||
|
||||
@@ -59,10 +58,10 @@ impl RealFetcher {
|
||||
// causing our program to deadlock. Once this is fixed, watcher no
|
||||
// longer needs to be optional, but is still maybe useful?
|
||||
let watcher = match watch_mode {
|
||||
WatchMode::Enabled => {
|
||||
Some(notify::watcher(notify_sender, Duration::from_millis(300))
|
||||
.expect("Couldn't start 'notify' file watcher"))
|
||||
}
|
||||
WatchMode::Enabled => Some(
|
||||
notify::watcher(notify_sender, Duration::from_millis(300))
|
||||
.expect("Couldn't start 'notify' file watcher"),
|
||||
),
|
||||
WatchMode::Disabled => None,
|
||||
};
|
||||
|
||||
@@ -152,4 +151,4 @@ impl ImfsFetcher for RealFetcher {
|
||||
fn receiver(&self) -> Receiver<ImfsEvent> {
|
||||
self.receiver.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,9 @@ impl ImfsSnapshot {
|
||||
|
||||
/// Create a new directory ImfsSnapshot with the given children.
|
||||
pub fn dir<S: Into<String>>(children: HashMap<S, ImfsSnapshot>) -> ImfsSnapshot {
|
||||
let children = children
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into(), v))
|
||||
.collect();
|
||||
let children = children.into_iter().map(|(k, v)| (k.into(), v)).collect();
|
||||
|
||||
ImfsSnapshot::Directory(DirectorySnapshot {
|
||||
children,
|
||||
})
|
||||
ImfsSnapshot::Directory(DirectorySnapshot { children })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +30,4 @@ pub struct FileSnapshot {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirectorySnapshot {
|
||||
pub children: HashMap<String, ImfsSnapshot>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@ macro_rules! impl_from {
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![recursion_limit="128"]
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
// Macros
|
||||
#[macro_use]
|
||||
@@ -16,4 +16,4 @@ mod serve_session;
|
||||
mod session_id;
|
||||
mod snapshot;
|
||||
mod snapshot_middleware;
|
||||
mod web;
|
||||
mod web;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
mem,
|
||||
sync::{
|
||||
RwLock,
|
||||
Mutex,
|
||||
},
|
||||
sync::{Mutex, RwLock},
|
||||
};
|
||||
|
||||
use futures::sync::oneshot;
|
||||
@@ -13,7 +10,10 @@ struct Listener<T> {
|
||||
cursor: u32,
|
||||
}
|
||||
|
||||
fn fire_listener_if_ready<T: Clone>(messages: &[T], listener: Listener<T>) -> Result<(), Listener<T>> {
|
||||
fn fire_listener_if_ready<T: Clone>(
|
||||
messages: &[T],
|
||||
listener: Listener<T>,
|
||||
) -> Result<(), Listener<T>> {
|
||||
let current_cursor = messages.len() as u32;
|
||||
|
||||
if listener.cursor < current_cursor {
|
||||
@@ -30,8 +30,8 @@ fn fire_listener_if_ready<T: Clone>(messages: &[T], listener: Listener<T>) -> Re
|
||||
/// Definitely non-optimal. This would ideally be a lockless mpmc queue.
|
||||
#[derive(Default)]
|
||||
pub struct MessageQueue<T> {
|
||||
messages: RwLock<Vec<T>>,
|
||||
message_listeners: Mutex<Vec<Listener<T>>>,
|
||||
messages: RwLock<Vec<T>>,
|
||||
message_listeners: Mutex<Vec<Listener<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Clone> MessageQueue<T> {
|
||||
@@ -52,7 +52,7 @@ impl<T: Clone> MessageQueue<T> {
|
||||
for listener in message_listeners.drain(..) {
|
||||
match fire_listener_if_ready(&messages, listener) {
|
||||
Ok(_) => {}
|
||||
Err(listener) => remaining_listeners.push(listener)
|
||||
Err(listener) => remaining_listeners.push(listener),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,13 @@ impl<T: Clone> MessageQueue<T> {
|
||||
|
||||
pub fn subscribe(&self, cursor: u32, sender: oneshot::Sender<(u32, Vec<T>)>) {
|
||||
let listener = {
|
||||
let listener = Listener {
|
||||
sender,
|
||||
cursor,
|
||||
};
|
||||
let listener = Listener { sender, cursor };
|
||||
|
||||
let messages = self.messages.read().unwrap();
|
||||
|
||||
match fire_listener_if_ready(&messages, listener) {
|
||||
Ok(_) => return,
|
||||
Err(listener) => listener
|
||||
Err(listener) => listener,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,4 +93,4 @@ impl<T: Clone> MessageQueue<T> {
|
||||
|
||||
(current_cursor, messages[(cursor as usize)..].to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::{
|
||||
path::{self, Path, PathBuf},
|
||||
collections::{HashMap, HashSet},
|
||||
path::{self, Path, PathBuf},
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
use log::warn;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct PathMapNode<T> {
|
||||
@@ -51,7 +51,9 @@ impl<T> PathMap<T> {
|
||||
}
|
||||
|
||||
pub fn children(&self, path: impl AsRef<Path>) -> Option<Vec<&Path>> {
|
||||
self.nodes.get(path.as_ref()).map(|v| v.children.iter().map(AsRef::as_ref).collect())
|
||||
self.nodes
|
||||
.get(path.as_ref())
|
||||
.map(|v| v.children.iter().map(AsRef::as_ref).collect())
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, path: impl AsRef<Path>) -> bool {
|
||||
@@ -76,10 +78,7 @@ impl<T> PathMap<T> {
|
||||
self.orphan_paths.remove(child);
|
||||
}
|
||||
|
||||
self.nodes.insert(path, PathMapNode {
|
||||
value,
|
||||
children,
|
||||
});
|
||||
self.nodes.insert(path, PathMapNode { value, children });
|
||||
}
|
||||
|
||||
/// Remove the given path and all of its linked descendants, returning all
|
||||
@@ -105,10 +104,13 @@ impl<T> PathMap<T> {
|
||||
for child in node.children.into_iter() {
|
||||
to_visit.push(child);
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
warn!("Consistency issue; tried to remove {} but it was already removed", path.display());
|
||||
},
|
||||
warn!(
|
||||
"Consistency issue; tried to remove {} but it was already removed",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,11 +125,16 @@ impl<T> PathMap<T> {
|
||||
/// FS events, a file remove event could be followed by that file's
|
||||
/// directory being removed, in which case we should process that
|
||||
/// directory's parent.
|
||||
pub fn descend(&self, start_path: impl Into<PathBuf>, target_path: impl AsRef<Path>) -> PathBuf {
|
||||
pub fn descend(
|
||||
&self,
|
||||
start_path: impl Into<PathBuf>,
|
||||
target_path: impl AsRef<Path>,
|
||||
) -> PathBuf {
|
||||
let start_path = start_path.into();
|
||||
let target_path = target_path.as_ref();
|
||||
|
||||
let relative_path = target_path.strip_prefix(&start_path)
|
||||
let relative_path = target_path
|
||||
.strip_prefix(&start_path)
|
||||
.expect("target_path did not begin with start_path");
|
||||
let mut current_path = start_path;
|
||||
|
||||
@@ -141,7 +148,7 @@ impl<T> PathMap<T> {
|
||||
} else {
|
||||
return current_path;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -219,9 +226,7 @@ mod test {
|
||||
|
||||
map.insert("/foo", 6);
|
||||
|
||||
assert_eq!(map.remove("/foo"), vec![
|
||||
(PathBuf::from("/foo"), 6),
|
||||
]);
|
||||
assert_eq!(map.remove("/foo"), vec![(PathBuf::from("/foo"), 6),]);
|
||||
|
||||
assert_eq!(map.get("/foo"), None);
|
||||
}
|
||||
@@ -233,10 +238,10 @@ mod test {
|
||||
map.insert("/foo", 6);
|
||||
map.insert("/foo/bar", 12);
|
||||
|
||||
assert_eq!(map.remove("/foo"), vec![
|
||||
(PathBuf::from("/foo"), 6),
|
||||
(PathBuf::from("/foo/bar"), 12),
|
||||
]);
|
||||
assert_eq!(
|
||||
map.remove("/foo"),
|
||||
vec![(PathBuf::from("/foo"), 6), (PathBuf::from("/foo/bar"), 12),]
|
||||
);
|
||||
|
||||
assert_eq!(map.get("/foo"), None);
|
||||
assert_eq!(map.get("/foo/bar"), None);
|
||||
@@ -250,11 +255,14 @@ mod test {
|
||||
map.insert("/foo/bar", 12);
|
||||
map.insert("/foo/bar/baz", 18);
|
||||
|
||||
assert_eq!(map.remove("/foo"), vec![
|
||||
(PathBuf::from("/foo"), 6),
|
||||
(PathBuf::from("/foo/bar"), 12),
|
||||
(PathBuf::from("/foo/bar/baz"), 18),
|
||||
]);
|
||||
assert_eq!(
|
||||
map.remove("/foo"),
|
||||
vec![
|
||||
(PathBuf::from("/foo"), 6),
|
||||
(PathBuf::from("/foo/bar"), 12),
|
||||
(PathBuf::from("/foo/bar/baz"), 18),
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(map.get("/foo"), None);
|
||||
assert_eq!(map.get("/foo/bar"), None);
|
||||
@@ -268,11 +276,9 @@ mod test {
|
||||
map.insert("/foo", 6);
|
||||
map.insert("/foo/bar/baz", 12);
|
||||
|
||||
assert_eq!(map.remove("/foo"), vec![
|
||||
(PathBuf::from("/foo"), 6),
|
||||
]);
|
||||
assert_eq!(map.remove("/foo"), vec![(PathBuf::from("/foo"), 6),]);
|
||||
|
||||
assert_eq!(map.get("/foo"), None);
|
||||
assert_eq!(map.get("/foo/bar/baz"), Some(&12));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,22 +33,27 @@ use std::path::{Component, Path};
|
||||
use serde::Serializer;
|
||||
|
||||
pub fn serialize_option<S, T>(maybe_path: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
T: AsRef<Path>,
|
||||
where
|
||||
S: Serializer,
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
match maybe_path {
|
||||
Some(path) => serialize(path, serializer),
|
||||
None => serializer.serialize_none()
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S, T>(path: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
T: AsRef<Path>,
|
||||
where
|
||||
S: Serializer,
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
|
||||
assert!(path.is_relative(), "path_serializer can only handle relative paths");
|
||||
assert!(
|
||||
path.is_relative(),
|
||||
"path_serializer can only handle relative paths"
|
||||
);
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
@@ -66,4 +71,4 @@ pub fn serialize<S, T>(path: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
}
|
||||
|
||||
serializer.serialize_str(&output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, BTreeMap},
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
fmt,
|
||||
fs::{self, File},
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use log::warn;
|
||||
use failure::Fail;
|
||||
use rbx_dom_weak::{UnresolvedRbxValue, RbxValue};
|
||||
use serde::{Serialize, Serializer, Deserialize};
|
||||
use log::warn;
|
||||
use rbx_dom_weak::{RbxValue, UnresolvedRbxValue};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
|
||||
static DEFAULT_PLACE: &'static str = include_str!("../assets/place.project.json");
|
||||
|
||||
@@ -40,7 +40,8 @@ impl SourceProject {
|
||||
/// Consumes the SourceProject and yields a Project, ready for prime-time.
|
||||
pub fn into_project(mut self, project_file_location: &Path) -> Project {
|
||||
let tree = self.tree.into_project_node(project_file_location);
|
||||
let plugins = self.plugins
|
||||
let plugins = self
|
||||
.plugins
|
||||
.drain(..)
|
||||
.map(|source_plugin| source_plugin.into_plugin(project_file_location))
|
||||
.collect();
|
||||
@@ -76,43 +77,48 @@ impl SourceProject {
|
||||
///
|
||||
/// This holds true for other values that might be ambiguous or just have more
|
||||
/// complicated representations like enums.
|
||||
fn serialize_unresolved_minimal<S>(unresolved: &UnresolvedRbxValue, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
fn serialize_unresolved_minimal<S>(
|
||||
unresolved: &UnresolvedRbxValue,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match unresolved {
|
||||
UnresolvedRbxValue::Ambiguous(_) => unresolved.serialize(serializer),
|
||||
UnresolvedRbxValue::Concrete(concrete) => {
|
||||
match concrete {
|
||||
RbxValue::Bool { value } => value.serialize(serializer),
|
||||
RbxValue::CFrame { value } => value.serialize(serializer),
|
||||
RbxValue::Color3 { value } => value.serialize(serializer),
|
||||
RbxValue::Color3uint8 { value } => value.serialize(serializer),
|
||||
RbxValue::Content { value } => value.serialize(serializer),
|
||||
RbxValue::Float32 { value } => value.serialize(serializer),
|
||||
RbxValue::Int32 { value } => value.serialize(serializer),
|
||||
RbxValue::String { value } => value.serialize(serializer),
|
||||
RbxValue::UDim { value } => value.serialize(serializer),
|
||||
RbxValue::UDim2 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector2 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector2int16 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector3 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector3int16 { value } => value.serialize(serializer),
|
||||
_ => concrete.serialize(serializer),
|
||||
}
|
||||
UnresolvedRbxValue::Concrete(concrete) => match concrete {
|
||||
RbxValue::Bool { value } => value.serialize(serializer),
|
||||
RbxValue::CFrame { value } => value.serialize(serializer),
|
||||
RbxValue::Color3 { value } => value.serialize(serializer),
|
||||
RbxValue::Color3uint8 { value } => value.serialize(serializer),
|
||||
RbxValue::Content { value } => value.serialize(serializer),
|
||||
RbxValue::Float32 { value } => value.serialize(serializer),
|
||||
RbxValue::Int32 { value } => value.serialize(serializer),
|
||||
RbxValue::String { value } => value.serialize(serializer),
|
||||
RbxValue::UDim { value } => value.serialize(serializer),
|
||||
RbxValue::UDim2 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector2 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector2int16 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector3 { value } => value.serialize(serializer),
|
||||
RbxValue::Vector3int16 { value } => value.serialize(serializer),
|
||||
_ => concrete.serialize(serializer),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around serialize_unresolved_minimal that handles the HashMap case.
|
||||
fn serialize_unresolved_map<S>(value: &HashMap<String, UnresolvedRbxValue>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
fn serialize_unresolved_map<S>(
|
||||
value: &HashMap<String, UnresolvedRbxValue>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Minimal<'a>(
|
||||
#[serde(serialize_with = "serialize_unresolved_minimal")]
|
||||
&'a UnresolvedRbxValue
|
||||
#[serde(serialize_with = "serialize_unresolved_minimal")] &'a UnresolvedRbxValue,
|
||||
);
|
||||
|
||||
let mut map = serializer.serialize_map(Some(value.len()))?;
|
||||
@@ -135,11 +141,14 @@ struct SourceProjectNode {
|
||||
rename = "$properties",
|
||||
default = "HashMap::new",
|
||||
skip_serializing_if = "HashMap::is_empty",
|
||||
serialize_with = "serialize_unresolved_map",
|
||||
serialize_with = "serialize_unresolved_map"
|
||||
)]
|
||||
properties: HashMap<String, UnresolvedRbxValue>,
|
||||
|
||||
#[serde(rename = "$ignoreUnknownInstances", skip_serializing_if = "Option::is_none")]
|
||||
#[serde(
|
||||
rename = "$ignoreUnknownInstances",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
ignore_unknown_instances: Option<bool>,
|
||||
|
||||
#[serde(rename = "$path", skip_serializing_if = "Option::is_none")]
|
||||
@@ -152,8 +161,15 @@ struct SourceProjectNode {
|
||||
impl SourceProjectNode {
|
||||
/// Consumes the SourceProjectNode and turns it into a ProjectNode.
|
||||
pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode {
|
||||
let children = self.children.iter()
|
||||
.map(|(key, value)| (key.clone(), value.clone().into_project_node(project_file_location)))
|
||||
let children = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
key.clone(),
|
||||
value.clone().into_project_node(project_file_location),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Make sure that paths are absolute, transforming them by adding the
|
||||
@@ -191,9 +207,7 @@ impl SourcePlugin {
|
||||
project_folder_location.join(self.path)
|
||||
};
|
||||
|
||||
Plugin {
|
||||
path,
|
||||
}
|
||||
Plugin { path }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,15 +233,16 @@ impl fmt::Display for ProjectLoadError {
|
||||
use self::ProjectLoadError::*;
|
||||
|
||||
match self {
|
||||
NotFound => {
|
||||
write!(formatter, "Project file not found")
|
||||
}
|
||||
NotFound => write!(formatter, "Project file not found"),
|
||||
Io { inner, path } => {
|
||||
write!(formatter, "I/O error: {} in path {}", inner, path.display())
|
||||
}
|
||||
Json { inner, path } => {
|
||||
write!(formatter, "JSON error: {} in path {}", inner, path.display())
|
||||
}
|
||||
Json { inner, path } => write!(
|
||||
formatter,
|
||||
"JSON error: {} in path {}",
|
||||
inner,
|
||||
path.display()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +259,9 @@ pub enum ProjectInitError {
|
||||
impl fmt::Display for ProjectInitError {
|
||||
fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ProjectInitError::AlreadyExists(path) => write!(output, "Path {} already exists", path.display()),
|
||||
ProjectInitError::AlreadyExists(path) => {
|
||||
write!(output, "Path {} already exists", path.display())
|
||||
}
|
||||
ProjectInitError::IoError(inner) => write!(output, "IO error: {}", inner),
|
||||
ProjectInitError::SaveError(inner) => write!(output, "{}", inner),
|
||||
ProjectInitError::JsonError(inner) => write!(output, "{}", inner),
|
||||
@@ -277,8 +294,13 @@ impl ProjectNode {
|
||||
fn validate_reserved_names(&self) {
|
||||
for (name, child) in &self.children {
|
||||
if name.starts_with('$') {
|
||||
warn!("Keys starting with '$' are reserved by Rojo to ensure forward compatibility.");
|
||||
warn!("This project uses the key '{}', which should be renamed.", name);
|
||||
warn!(
|
||||
"Keys starting with '$' are reserved by Rojo to ensure forward compatibility."
|
||||
);
|
||||
warn!(
|
||||
"This project uses the key '{}', which should be renamed.",
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
child.validate_reserved_names();
|
||||
@@ -286,7 +308,9 @@ impl ProjectNode {
|
||||
}
|
||||
|
||||
fn to_source_node(&self, project_file_location: &Path) -> SourceProjectNode {
|
||||
let children = self.children.iter()
|
||||
let children = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|(key, value)| (key.clone(), value.to_source_node(project_file_location)))
|
||||
.collect();
|
||||
|
||||
@@ -329,9 +353,7 @@ impl Plugin {
|
||||
Err(_) => format!("{}", self.path.display()),
|
||||
};
|
||||
|
||||
SourcePlugin {
|
||||
path,
|
||||
}
|
||||
SourcePlugin { path }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,13 +373,18 @@ impl Project {
|
||||
|
||||
let project_name = if project_fuzzy_path == project_path {
|
||||
project_fuzzy_path
|
||||
.parent().expect("Path did not have a parent directory")
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
.parent()
|
||||
.expect("Path did not have a parent directory")
|
||||
.file_name()
|
||||
.expect("Path did not have a file name")
|
||||
.to_str()
|
||||
.expect("Path had invalid Unicode")
|
||||
} else {
|
||||
project_fuzzy_path
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
.file_name()
|
||||
.expect("Path did not have a file name")
|
||||
.to_str()
|
||||
.expect("Path had invalid Unicode")
|
||||
};
|
||||
|
||||
let mut project = Project::load_from_str(DEFAULT_PLACE, &project_path)
|
||||
@@ -365,8 +392,7 @@ impl Project {
|
||||
|
||||
project.name = project_name.to_owned();
|
||||
|
||||
project.save()
|
||||
.map_err(ProjectInitError::SaveError)?;
|
||||
project.save().map_err(ProjectInitError::SaveError)?;
|
||||
|
||||
Ok(project_path)
|
||||
}
|
||||
@@ -376,17 +402,23 @@ impl Project {
|
||||
|
||||
let project_name = if project_fuzzy_path == project_path {
|
||||
project_fuzzy_path
|
||||
.parent().expect("Path did not have a parent directory")
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
.parent()
|
||||
.expect("Path did not have a parent directory")
|
||||
.file_name()
|
||||
.expect("Path did not have a file name")
|
||||
.to_str()
|
||||
.expect("Path had invalid Unicode")
|
||||
} else {
|
||||
project_fuzzy_path
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
.file_name()
|
||||
.expect("Path did not have a file name")
|
||||
.to_str()
|
||||
.expect("Path had invalid Unicode")
|
||||
};
|
||||
|
||||
let project_folder_path = project_path
|
||||
.parent().expect("Path did not have a parent directory");
|
||||
.parent()
|
||||
.expect("Path did not have a parent directory");
|
||||
|
||||
let tree = ProjectNode {
|
||||
path: Some(project_folder_path.join("src")),
|
||||
@@ -402,8 +434,7 @@ impl Project {
|
||||
file_location: project_path.clone(),
|
||||
};
|
||||
|
||||
project.save()
|
||||
.map_err(ProjectInitError::SaveError)?;
|
||||
project.save().map_err(ProjectInitError::SaveError)?;
|
||||
|
||||
Ok(project_path)
|
||||
}
|
||||
@@ -419,7 +450,7 @@ impl Project {
|
||||
|
||||
match fs::metadata(&project_path) {
|
||||
Err(error) => match error.kind() {
|
||||
io::ErrorKind::NotFound => {},
|
||||
io::ErrorKind::NotFound => {}
|
||||
_ => return Err(ProjectInitError::IoError(error)),
|
||||
},
|
||||
Ok(_) => return Err(ProjectInitError::AlreadyExists(project_path)),
|
||||
@@ -459,13 +490,19 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_from_str(contents: &str, project_file_location: &Path) -> Result<Project, serde_json::Error> {
|
||||
fn load_from_str(
|
||||
contents: &str,
|
||||
project_file_location: &Path,
|
||||
) -> Result<Project, serde_json::Error> {
|
||||
let parsed: SourceProject = serde_json::from_str(&contents)?;
|
||||
|
||||
Ok(parsed.into_project(project_file_location))
|
||||
}
|
||||
|
||||
pub fn load_from_slice(contents: &[u8], project_file_location: &Path) -> Result<Project, serde_json::Error> {
|
||||
pub fn load_from_slice(
|
||||
contents: &[u8],
|
||||
project_file_location: &Path,
|
||||
) -> Result<Project, serde_json::Error> {
|
||||
let parsed: SourceProject = serde_json::from_slice(&contents)?;
|
||||
|
||||
Ok(parsed.into_project(project_file_location))
|
||||
@@ -481,17 +518,17 @@ impl Project {
|
||||
}
|
||||
|
||||
pub fn load_exact(project_file_location: &Path) -> Result<Project, ProjectLoadError> {
|
||||
let contents = fs::read_to_string(project_file_location)
|
||||
.map_err(|error| match error.kind() {
|
||||
let contents =
|
||||
fs::read_to_string(project_file_location).map_err(|error| match error.kind() {
|
||||
io::ErrorKind::NotFound => ProjectLoadError::NotFound,
|
||||
_ => ProjectLoadError::Io {
|
||||
inner: error,
|
||||
path: project_file_location.to_path_buf(),
|
||||
}
|
||||
},
|
||||
})?;
|
||||
|
||||
let parsed: SourceProject = serde_json::from_str(&contents)
|
||||
.map_err(|error| ProjectLoadError::Json {
|
||||
let parsed: SourceProject =
|
||||
serde_json::from_str(&contents).map_err(|error| ProjectLoadError::Json {
|
||||
inner: error,
|
||||
path: project_file_location.to_path_buf(),
|
||||
})?;
|
||||
@@ -504,8 +541,7 @@ impl Project {
|
||||
|
||||
pub fn save(&self) -> Result<(), ProjectSaveError> {
|
||||
let source_project = self.to_source_project();
|
||||
let mut file = File::create(&self.file_location)
|
||||
.map_err(ProjectSaveError::IoError)?;
|
||||
let mut file = File::create(&self.file_location).map_err(ProjectSaveError::IoError)?;
|
||||
|
||||
serde_json::to_writer_pretty(&mut file, &source_project)
|
||||
.map_err(ProjectSaveError::JsonError)?;
|
||||
@@ -516,9 +552,12 @@ impl Project {
|
||||
/// Checks if there are any compatibility issues with this project file and
|
||||
/// warns the user if there are any.
|
||||
fn check_compatibility(&self) {
|
||||
let file_name = self.file_location
|
||||
.file_name().expect("Project file path did not have a file name")
|
||||
.to_str().expect("Project file path was not valid Unicode");
|
||||
let file_name = self
|
||||
.file_location
|
||||
.file_name()
|
||||
.expect("Project file path did not have a file name")
|
||||
.to_str()
|
||||
.expect("Project file path was not valid Unicode");
|
||||
|
||||
if file_name == COMPAT_PROJECT_FILENAME {
|
||||
warn!("Rojo's default project file name changed in 0.5.0-alpha3.");
|
||||
@@ -554,7 +593,8 @@ impl Project {
|
||||
}
|
||||
|
||||
fn to_source_project(&self) -> SourceProject {
|
||||
let plugins = self.plugins
|
||||
let plugins = self
|
||||
.plugins
|
||||
.iter()
|
||||
.map(|plugin| plugin.to_source_plugin(&self.file_location))
|
||||
.collect();
|
||||
@@ -567,4 +607,4 @@ impl Project {
|
||||
serve_place_ids: self.serve_place_ids.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{
|
||||
project::Project,
|
||||
session_id::SessionId,
|
||||
};
|
||||
use crate::{project::Project, session_id::SessionId};
|
||||
|
||||
/// Contains all of the state for a Rojo serve session.
|
||||
pub struct ServeSession {
|
||||
@@ -30,4 +27,4 @@ impl ServeSession {
|
||||
.as_ref()
|
||||
.and_then(|project| project.serve_place_ids.as_ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@@ -8,4 +8,4 @@ impl SessionId {
|
||||
pub fn new() -> SessionId {
|
||||
SessionId(Uuid::new_v4())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
//! Defines the structure of an instance snapshot.
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId, RbxValue};
|
||||
use rbx_dom_weak::{RbxId, RbxTree, RbxValue};
|
||||
|
||||
/// A lightweight description of what an instance should look like. Attempts to
|
||||
/// be somewhat memory efficient by borrowing from its source data, indicated by
|
||||
@@ -22,13 +19,14 @@ pub struct InstanceSnapshot<'source> {
|
||||
pub class_name: Cow<'source, str>,
|
||||
pub properties: HashMap<String, RbxValue>,
|
||||
pub children: Vec<InstanceSnapshot<'source>>,
|
||||
|
||||
// TODO: Snapshot source, like a file or a project node?
|
||||
}
|
||||
|
||||
impl<'source> InstanceSnapshot<'source> {
|
||||
pub fn get_owned(&'source self) -> InstanceSnapshot<'static> {
|
||||
let children: Vec<InstanceSnapshot<'static>> = self.children.iter()
|
||||
let children: Vec<InstanceSnapshot<'static>> = self
|
||||
.children
|
||||
.iter()
|
||||
.map(InstanceSnapshot::get_owned)
|
||||
.collect();
|
||||
|
||||
@@ -42,10 +40,12 @@ impl<'source> InstanceSnapshot<'source> {
|
||||
}
|
||||
|
||||
pub fn from_tree(tree: &RbxTree, id: RbxId) -> InstanceSnapshot<'static> {
|
||||
let instance = tree.get_instance(id)
|
||||
let instance = tree
|
||||
.get_instance(id)
|
||||
.expect("instance did not exist in tree");
|
||||
|
||||
let children = instance.get_children_ids()
|
||||
let children = instance
|
||||
.get_children_ids()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|id| InstanceSnapshot::from_tree(tree, id))
|
||||
@@ -59,4 +59,4 @@ impl<'source> InstanceSnapshot<'source> {
|
||||
children,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod instance_snapshot;
|
||||
mod patch;
|
||||
mod patch_apply;
|
||||
mod patch_compute;
|
||||
mod instance_snapshot;
|
||||
|
||||
pub use instance_snapshot::InstanceSnapshot;
|
||||
pub use patch::*;
|
||||
pub use patch_apply::apply_patch_set;
|
||||
pub use patch_compute::compute_patch_set;
|
||||
pub use patch::*;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rbx_dom_weak::{RbxValue, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxValue};
|
||||
|
||||
use super::InstanceSnapshot;
|
||||
|
||||
@@ -41,4 +41,4 @@ pub struct PatchUpdateInstance {
|
||||
/// Contains all changed properties. If a property is assigned to `None`,
|
||||
/// then that property has been removed.
|
||||
pub changed_properties: HashMap<String, Option<RbxValue>>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxValue, RbxId, RbxInstanceProperties};
|
||||
use rbx_dom_weak::{RbxId, RbxInstanceProperties, RbxTree, RbxValue};
|
||||
|
||||
use super::{
|
||||
patch::{PatchSet, PatchUpdateInstance},
|
||||
InstanceSnapshot,
|
||||
};
|
||||
|
||||
pub fn apply_patch_set(
|
||||
tree: &mut RbxTree,
|
||||
patch_set: &PatchSet,
|
||||
) {
|
||||
pub fn apply_patch_set(tree: &mut RbxTree, patch_set: &PatchSet) {
|
||||
let mut context = PatchApplyContext::default();
|
||||
|
||||
for removed_id in &patch_set.removed_instances {
|
||||
@@ -47,13 +44,16 @@ struct PatchApplyContext {
|
||||
/// then apply properties all at once at the end.
|
||||
fn apply_deferred_properties(context: PatchApplyContext, tree: &mut RbxTree) {
|
||||
for (id, mut properties) in context.properties_to_apply {
|
||||
let instance = tree.get_instance_mut(id)
|
||||
let instance = tree
|
||||
.get_instance_mut(id)
|
||||
.expect("Invalid instance ID in deferred property map");
|
||||
|
||||
for property_value in properties.values_mut() {
|
||||
if let RbxValue::Ref { value: Some(id) } = property_value {
|
||||
if let Some(&instance_id) = context.snapshot_id_to_instance_id.get(id) {
|
||||
*property_value = RbxValue::Ref { value: Some(instance_id) };
|
||||
*property_value = RbxValue::Ref {
|
||||
value: Some(instance_id),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,9 @@ fn apply_add_child(
|
||||
|
||||
let id = tree.insert_instance(properties, parent_id);
|
||||
|
||||
context.properties_to_apply.insert(id, snapshot.properties.clone());
|
||||
context
|
||||
.properties_to_apply
|
||||
.insert(id, snapshot.properties.clone());
|
||||
|
||||
if let Some(snapshot_id) = snapshot.snapshot_id {
|
||||
context.snapshot_id_to_instance_id.insert(snapshot_id, id);
|
||||
@@ -95,7 +97,8 @@ fn apply_update_child(
|
||||
tree: &mut RbxTree,
|
||||
patch: &PatchUpdateInstance,
|
||||
) {
|
||||
let instance = tree.get_instance_mut(patch.id)
|
||||
let instance = tree
|
||||
.get_instance_mut(patch.id)
|
||||
.expect("Instance referred to by patch does not exist");
|
||||
|
||||
if let Some(name) = &patch.changed_name {
|
||||
@@ -114,9 +117,12 @@ fn apply_update_child(
|
||||
Some(RbxValue::Ref { value: Some(id) }) => {
|
||||
let new_id = context.snapshot_id_to_instance_id.get(id).unwrap_or(id);
|
||||
|
||||
instance.properties.insert(key.clone(), RbxValue::Ref {
|
||||
value: Some(*new_id),
|
||||
});
|
||||
instance.properties.insert(
|
||||
key.clone(),
|
||||
RbxValue::Ref {
|
||||
value: Some(*new_id),
|
||||
},
|
||||
);
|
||||
}
|
||||
Some(value) => {
|
||||
instance.properties.insert(key.clone(), value.clone());
|
||||
@@ -132,10 +138,7 @@ fn apply_update_child(
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::RbxValue;
|
||||
@@ -165,12 +168,10 @@ mod test {
|
||||
};
|
||||
|
||||
let patch_set = PatchSet {
|
||||
added_instances: vec![
|
||||
PatchAddInstance {
|
||||
parent_id: root_id,
|
||||
instance: snapshot.clone(),
|
||||
}
|
||||
],
|
||||
added_instances: vec![PatchAddInstance {
|
||||
parent_id: root_id,
|
||||
instance: snapshot.clone(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -236,4 +237,4 @@ mod test {
|
||||
assert_eq!(root_instance.class_name, "NewClassName");
|
||||
assert_eq!(root_instance.properties, expected_properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxValue, RbxId, RbxInstance};
|
||||
use rbx_dom_weak::{RbxId, RbxInstance, RbxTree, RbxValue};
|
||||
|
||||
use super::{
|
||||
patch::{PatchAddInstance, PatchSet, PatchUpdateInstance},
|
||||
InstanceSnapshot,
|
||||
patch::{PatchSet, PatchAddInstance, PatchUpdateInstance},
|
||||
};
|
||||
|
||||
pub fn compute_patch_set<'a>(
|
||||
@@ -38,7 +38,9 @@ fn rewrite_refs_in_updates(context: &ComputePatchContext, updates: &mut [PatchUp
|
||||
for property_value in update.changed_properties.values_mut() {
|
||||
if let Some(RbxValue::Ref { value: Some(id) }) = property_value {
|
||||
if let Some(&instance_id) = context.snapshot_id_to_instance_id.get(id) {
|
||||
*property_value = Some(RbxValue::Ref { value: Some(instance_id) });
|
||||
*property_value = Some(RbxValue::Ref {
|
||||
value: Some(instance_id),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +57,9 @@ fn rewrite_refs_in_snapshot(context: &ComputePatchContext, snapshot: &mut Instan
|
||||
for property_value in snapshot.properties.values_mut() {
|
||||
if let RbxValue::Ref { value: Some(id) } = property_value {
|
||||
if let Some(&instance_id) = context.snapshot_id_to_instance_id.get(id) {
|
||||
*property_value = RbxValue::Ref { value: Some(instance_id) };
|
||||
*property_value = RbxValue::Ref {
|
||||
value: Some(instance_id),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +80,8 @@ fn compute_patch_set_internal<'a>(
|
||||
context.snapshot_id_to_instance_id.insert(snapshot_id, id);
|
||||
}
|
||||
|
||||
let instance = tree.get_instance(id)
|
||||
let instance = tree
|
||||
.get_instance(id)
|
||||
.expect("Instance did not exist in tree");
|
||||
|
||||
compute_property_patches(snapshot, instance, patch_set);
|
||||
@@ -145,7 +150,8 @@ fn compute_children_patches<'a>(
|
||||
id: RbxId,
|
||||
patch_set: &mut PatchSet<'a>,
|
||||
) {
|
||||
let instance = tree.get_instance(id)
|
||||
let instance = tree
|
||||
.get_instance(id)
|
||||
.expect("Instance did not exist in tree");
|
||||
|
||||
let instance_children = instance.get_children_ids();
|
||||
@@ -153,30 +159,38 @@ fn compute_children_patches<'a>(
|
||||
let mut paired_instances = vec![false; instance_children.len()];
|
||||
|
||||
for snapshot_child in snapshot.children.iter() {
|
||||
let matching_instance = instance_children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(instance_index, instance_child_id)| {
|
||||
if paired_instances[*instance_index] {
|
||||
return false;
|
||||
}
|
||||
let matching_instance =
|
||||
instance_children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(instance_index, instance_child_id)| {
|
||||
if paired_instances[*instance_index] {
|
||||
return false;
|
||||
}
|
||||
|
||||
let instance_child = tree.get_instance(**instance_child_id)
|
||||
.expect("Instance did not exist in tree");
|
||||
let instance_child = tree
|
||||
.get_instance(**instance_child_id)
|
||||
.expect("Instance did not exist in tree");
|
||||
|
||||
if snapshot_child.name == instance_child.name &&
|
||||
instance_child.class_name == instance_child.class_name
|
||||
{
|
||||
paired_instances[*instance_index] = true;
|
||||
return true;
|
||||
}
|
||||
if snapshot_child.name == instance_child.name
|
||||
&& instance_child.class_name == instance_child.class_name
|
||||
{
|
||||
paired_instances[*instance_index] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
false
|
||||
});
|
||||
|
||||
match matching_instance {
|
||||
Some((_, instance_child_id)) => {
|
||||
compute_patch_set_internal(context, snapshot_child, tree, *instance_child_id, patch_set);
|
||||
compute_patch_set_internal(
|
||||
context,
|
||||
snapshot_child,
|
||||
tree,
|
||||
*instance_child_id,
|
||||
patch_set,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
patch_set.added_instances.push(PatchAddInstance {
|
||||
@@ -238,18 +252,16 @@ mod test {
|
||||
let patch_set = compute_patch_set(&snapshot, &tree, root_id);
|
||||
|
||||
let expected_patch_set = PatchSet {
|
||||
updated_instances: vec![
|
||||
PatchUpdateInstance {
|
||||
id: root_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: hashmap! {
|
||||
"Self".to_owned() => Some(RbxValue::Ref {
|
||||
value: Some(root_id),
|
||||
}),
|
||||
},
|
||||
updated_instances: vec![PatchUpdateInstance {
|
||||
id: root_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: hashmap! {
|
||||
"Self".to_owned() => Some(RbxValue::Ref {
|
||||
value: Some(root_id),
|
||||
}),
|
||||
},
|
||||
],
|
||||
}],
|
||||
added_instances: Vec::new(),
|
||||
removed_instances: Vec::new(),
|
||||
};
|
||||
@@ -274,20 +286,18 @@ mod test {
|
||||
let snapshot_id = RbxId::new();
|
||||
let snapshot = InstanceSnapshot {
|
||||
snapshot_id: Some(snapshot_id),
|
||||
children: vec![
|
||||
InstanceSnapshot {
|
||||
properties: hashmap! {
|
||||
"Self".to_owned() => RbxValue::Ref {
|
||||
value: Some(snapshot_id),
|
||||
},
|
||||
children: vec![InstanceSnapshot {
|
||||
properties: hashmap! {
|
||||
"Self".to_owned() => RbxValue::Ref {
|
||||
value: Some(snapshot_id),
|
||||
},
|
||||
},
|
||||
|
||||
snapshot_id: None,
|
||||
name: Cow::Borrowed("child"),
|
||||
class_name: Cow::Borrowed("child"),
|
||||
children: Vec::new(),
|
||||
}
|
||||
],
|
||||
snapshot_id: None,
|
||||
name: Cow::Borrowed("child"),
|
||||
class_name: Cow::Borrowed("child"),
|
||||
children: Vec::new(),
|
||||
}],
|
||||
|
||||
properties: HashMap::new(),
|
||||
name: Cow::Borrowed("foo"),
|
||||
@@ -297,26 +307,24 @@ mod test {
|
||||
let patch_set = compute_patch_set(&snapshot, &tree, root_id);
|
||||
|
||||
let expected_patch_set = PatchSet {
|
||||
added_instances: vec![
|
||||
PatchAddInstance {
|
||||
parent_id: root_id,
|
||||
instance: InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
properties: hashmap! {
|
||||
"Self".to_owned() => RbxValue::Ref {
|
||||
value: Some(root_id),
|
||||
},
|
||||
added_instances: vec![PatchAddInstance {
|
||||
parent_id: root_id,
|
||||
instance: InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
properties: hashmap! {
|
||||
"Self".to_owned() => RbxValue::Ref {
|
||||
value: Some(root_id),
|
||||
},
|
||||
name: Cow::Borrowed("child"),
|
||||
class_name: Cow::Borrowed("child"),
|
||||
children: Vec::new(),
|
||||
},
|
||||
name: Cow::Borrowed("child"),
|
||||
class_name: Cow::Borrowed("child"),
|
||||
children: Vec::new(),
|
||||
},
|
||||
],
|
||||
}],
|
||||
updated_instances: Vec::new(),
|
||||
removed_instances: Vec::new(),
|
||||
};
|
||||
|
||||
assert_eq!(patch_set, expected_patch_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ pub struct InstanceSnapshotContext {
|
||||
pub plugin_context: Option<()>,
|
||||
}
|
||||
|
||||
pub struct ImfsSnapshotContext;
|
||||
pub struct ImfsSnapshotContext;
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::BTreeMap};
|
||||
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::{RbxTree, RbxValue, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree, RbxValue};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{Imfs, ImfsEntry, ImfsFetcher},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotCsv;
|
||||
|
||||
@@ -27,16 +22,18 @@ impl SnapshotMiddleware for SnapshotCsv {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_name = entry.path()
|
||||
.file_name().unwrap().to_string_lossy();
|
||||
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
||||
|
||||
if !file_name.ends_with(".csv") {
|
||||
return Ok(None);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let instance_name = entry.path()
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
.to_string_lossy().to_string();
|
||||
let instance_name = entry
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("Could not extract file stem")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let table_contents = convert_localization_csv(entry.contents(imfs)?);
|
||||
|
||||
@@ -53,10 +50,7 @@ impl SnapshotMiddleware for SnapshotCsv {
|
||||
}))
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
_tree: &RbxTree,
|
||||
_id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(_tree: &RbxTree, _id: RbxId) -> SnapshotFileResult {
|
||||
unimplemented!("Snapshotting CSV localization tables");
|
||||
}
|
||||
}
|
||||
@@ -96,15 +90,12 @@ struct LocalizationEntry<'a> {
|
||||
fn convert_localization_csv(contents: &[u8]) -> String {
|
||||
let mut reader = csv::Reader::from_reader(contents);
|
||||
|
||||
let headers = reader.headers()
|
||||
.expect("TODO: Handle csv errors")
|
||||
.clone();
|
||||
let headers = reader.headers().expect("TODO: Handle csv errors").clone();
|
||||
|
||||
let mut records = Vec::new();
|
||||
|
||||
for record in reader.into_records() {
|
||||
let record = record
|
||||
.expect("TODO: Handle csv errors");
|
||||
let record = record.expect("TODO: Handle csv errors");
|
||||
|
||||
records.push(record);
|
||||
}
|
||||
@@ -137,8 +128,7 @@ fn convert_localization_csv(contents: &[u8]) -> String {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
serde_json::to_string(&entries)
|
||||
.expect("Could not encode JSON for localization table")
|
||||
serde_json::to_string(&entries).expect("Could not encode JSON for localization table")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -150,9 +140,11 @@ mod test {
|
||||
#[test]
|
||||
fn csv_from_imfs() {
|
||||
let mut imfs = Imfs::new(NoopFetcher);
|
||||
let file = ImfsSnapshot::file(r#"
|
||||
let file = ImfsSnapshot::file(
|
||||
r#"
|
||||
Key,Source,Context,Example,es
|
||||
Ack,Ack!,,An exclamation of despair,¡Ay!"#);
|
||||
Ack,Ack!,,An exclamation of despair,¡Ay!"#,
|
||||
);
|
||||
|
||||
imfs.load_from_snapshot("/foo.csv", file);
|
||||
|
||||
@@ -165,10 +157,13 @@ Ack,Ack!,,An exclamation of despair,¡Ay!"#);
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "LocalizationTable");
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Contents".to_owned() => RbxValue::String {
|
||||
value: expected_contents.to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Contents".to_owned() => RbxValue::String {
|
||||
value: expected_contents.to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsSnapshot, DirectorySnapshot, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{DirectorySnapshot, Imfs, ImfsEntry, ImfsFetcher, ImfsSnapshot},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
snapshot_from_imfs,
|
||||
snapshot_from_instance,
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware},
|
||||
snapshot_from_imfs, snapshot_from_instance,
|
||||
};
|
||||
|
||||
pub struct SnapshotDir;
|
||||
@@ -37,9 +33,13 @@ impl SnapshotMiddleware for SnapshotDir {
|
||||
}
|
||||
}
|
||||
|
||||
let instance_name = entry.path()
|
||||
.file_name().expect("Could not extract file name")
|
||||
.to_str().unwrap().to_string();
|
||||
let instance_name = entry
|
||||
.path()
|
||||
.file_name()
|
||||
.expect("Could not extract file name")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
Ok(Some(InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
@@ -50,10 +50,7 @@ impl SnapshotMiddleware for SnapshotDir {
|
||||
}))
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
tree: &RbxTree,
|
||||
id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(tree: &RbxTree, id: RbxId) -> SnapshotFileResult {
|
||||
let instance = tree.get_instance(id).unwrap();
|
||||
|
||||
if instance.class_name != "Folder" {
|
||||
@@ -68,9 +65,7 @@ impl SnapshotMiddleware for SnapshotDir {
|
||||
}
|
||||
}
|
||||
|
||||
let snapshot = ImfsSnapshot::Directory(DirectorySnapshot {
|
||||
children,
|
||||
});
|
||||
let snapshot = ImfsSnapshot::Directory(DirectorySnapshot { children });
|
||||
|
||||
Some((instance.name.clone(), snapshot))
|
||||
}
|
||||
@@ -123,4 +118,4 @@ mod test {
|
||||
assert_eq!(child.properties, HashMap::new());
|
||||
assert_eq!(child.children, Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
use std::{
|
||||
fmt,
|
||||
error::Error,
|
||||
path::PathBuf,
|
||||
};
|
||||
use std::{error::Error, fmt, path::PathBuf};
|
||||
|
||||
use crate::{
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
use crate::snapshot::InstanceSnapshot;
|
||||
|
||||
pub type SnapshotResult<'a> = Result<Option<InstanceSnapshot<'a>>, SnapshotError>;
|
||||
|
||||
@@ -43,9 +37,7 @@ impl SnapshotError {
|
||||
path: impl Into<PathBuf>,
|
||||
) -> SnapshotError {
|
||||
SnapshotError {
|
||||
detail: SnapshotErrorDetail::FileContentsBadUnicode {
|
||||
inner,
|
||||
},
|
||||
detail: SnapshotErrorDetail::FileContentsBadUnicode { inner },
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
@@ -70,9 +62,7 @@ impl fmt::Display for SnapshotError {
|
||||
pub enum SnapshotErrorDetail {
|
||||
FileDidNotExist,
|
||||
FileNameBadUnicode,
|
||||
FileContentsBadUnicode {
|
||||
inner: std::str::Utf8Error,
|
||||
},
|
||||
FileContentsBadUnicode { inner: std::str::Utf8Error },
|
||||
}
|
||||
|
||||
impl SnapshotErrorDetail {
|
||||
@@ -81,7 +71,7 @@ impl SnapshotErrorDetail {
|
||||
|
||||
match self {
|
||||
FileContentsBadUnicode { inner } => Some(inner),
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +83,9 @@ impl fmt::Display for SnapshotErrorDetail {
|
||||
match self {
|
||||
FileDidNotExist => write!(formatter, "file did not exist"),
|
||||
FileNameBadUnicode => write!(formatter, "file name had malformed Unicode"),
|
||||
FileContentsBadUnicode { inner } => write!(formatter, "file had malformed unicode: {}", inner),
|
||||
FileContentsBadUnicode { inner } => {
|
||||
write!(formatter, "file had malformed unicode: {}", inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use rbx_dom_weak::{RbxId, RbxTree, UnresolvedRbxValue};
|
||||
use rbx_reflection::try_resolve_value;
|
||||
use rbx_dom_weak::{RbxTree, RbxId, UnresolvedRbxValue};
|
||||
use serde::{Deserialize};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{Imfs, ImfsEntry, ImfsFetcher},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotJsonModel;
|
||||
|
||||
@@ -27,22 +22,30 @@ impl SnapshotMiddleware for SnapshotJsonModel {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_name = entry.path()
|
||||
.file_name().unwrap().to_string_lossy();
|
||||
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
||||
|
||||
let instance_name = match match_trailing(&file_name, ".model.json") {
|
||||
Some(name) => name.to_owned(),
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let instance: JsonModel = serde_json::from_slice(entry.contents(imfs)?)
|
||||
.expect("TODO: Handle serde_json errors");
|
||||
let instance: JsonModel =
|
||||
serde_json::from_slice(entry.contents(imfs)?).expect("TODO: Handle serde_json errors");
|
||||
|
||||
if let Some(json_name) = &instance.name {
|
||||
if json_name != &instance_name {
|
||||
log::warn!("Name from JSON model did not match its file name: {}", entry.path().display());
|
||||
log::warn!("In Rojo < alpha 14, this model is named \"{}\" (from its 'Name' property)", json_name);
|
||||
log::warn!("In Rojo >= alpha 14, this model is named \"{}\" (from its file name)", instance_name);
|
||||
log::warn!(
|
||||
"Name from JSON model did not match its file name: {}",
|
||||
entry.path().display()
|
||||
);
|
||||
log::warn!(
|
||||
"In Rojo < alpha 14, this model is named \"{}\" (from its 'Name' property)",
|
||||
json_name
|
||||
);
|
||||
log::warn!(
|
||||
"In Rojo >= alpha 14, this model is named \"{}\" (from its file name)",
|
||||
instance_name
|
||||
);
|
||||
log::warn!("'Name' for the top-level instance in a JSON model is now optional and will be ignored.");
|
||||
}
|
||||
}
|
||||
@@ -52,10 +55,7 @@ impl SnapshotMiddleware for SnapshotJsonModel {
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
_tree: &RbxTree,
|
||||
_id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(_tree: &RbxTree, _id: RbxId) -> SnapshotFileResult {
|
||||
unimplemented!("Snapshotting models");
|
||||
}
|
||||
}
|
||||
@@ -103,14 +103,17 @@ impl JsonModelCore {
|
||||
fn into_snapshot(self, name: String) -> InstanceSnapshot<'static> {
|
||||
let class_name = self.class_name;
|
||||
|
||||
let children = self.children.into_iter()
|
||||
let children = self
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|child| child.core.into_snapshot(child.name))
|
||||
.collect();
|
||||
|
||||
let properties = self.properties.into_iter()
|
||||
let properties = self
|
||||
.properties
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
try_resolve_value(&class_name, &key, &value)
|
||||
.map(|resolved| (key, resolved))
|
||||
try_resolve_value(&class_name, &key, &value).map(|resolved| (key, resolved))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, _>>()
|
||||
.expect("TODO: Handle rbx_reflection errors");
|
||||
@@ -137,7 +140,8 @@ mod test {
|
||||
#[test]
|
||||
fn model_from_imfs() {
|
||||
let mut imfs = Imfs::new(NoopFetcher);
|
||||
let file = ImfsSnapshot::file(r#"
|
||||
let file = ImfsSnapshot::file(
|
||||
r#"
|
||||
{
|
||||
"Name": "children",
|
||||
"ClassName": "IntValue",
|
||||
@@ -151,31 +155,35 @@ mod test {
|
||||
}
|
||||
]
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
|
||||
imfs.load_from_snapshot("/foo.model.json", file);
|
||||
|
||||
let entry = imfs.get("/foo.model.json").unwrap();
|
||||
let instance_snapshot = SnapshotJsonModel::from_imfs(&mut imfs, &entry).unwrap().unwrap();
|
||||
let instance_snapshot = SnapshotJsonModel::from_imfs(&mut imfs, &entry)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(instance_snapshot, InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
name: Cow::Borrowed("foo"),
|
||||
class_name: Cow::Borrowed("IntValue"),
|
||||
properties: hashmap! {
|
||||
"Value".to_owned() => RbxValue::Int32 {
|
||||
value: 5,
|
||||
assert_eq!(
|
||||
instance_snapshot,
|
||||
InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
name: Cow::Borrowed("foo"),
|
||||
class_name: Cow::Borrowed("IntValue"),
|
||||
properties: hashmap! {
|
||||
"Value".to_owned() => RbxValue::Int32 {
|
||||
value: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
children: vec![
|
||||
InstanceSnapshot {
|
||||
children: vec![InstanceSnapshot {
|
||||
snapshot_id: None,
|
||||
name: Cow::Borrowed("The Child"),
|
||||
class_name: Cow::Borrowed("StringValue"),
|
||||
properties: HashMap::new(),
|
||||
children: Vec::new(),
|
||||
},
|
||||
],
|
||||
});
|
||||
},],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
str,
|
||||
};
|
||||
use std::{borrow::Cow, str};
|
||||
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::{RbxTree, RbxValue, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree, RbxValue};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsFetcher, ImfsEntry, FsResultExt},
|
||||
imfs::new::{FsResultExt, Imfs, ImfsEntry, ImfsFetcher},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotLua;
|
||||
|
||||
@@ -22,8 +17,7 @@ impl SnapshotMiddleware for SnapshotLua {
|
||||
imfs: &mut Imfs<F>,
|
||||
entry: &ImfsEntry,
|
||||
) -> SnapshotInstanceResult<'static> {
|
||||
let file_name = entry.path()
|
||||
.file_name().unwrap().to_string_lossy();
|
||||
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
||||
|
||||
if entry.is_directory() {
|
||||
let module_init_path = entry.path().join("init.lua");
|
||||
@@ -54,15 +48,16 @@ impl SnapshotMiddleware for SnapshotLua {
|
||||
}
|
||||
}
|
||||
|
||||
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 (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(imfs)?;
|
||||
let contents_str = str::from_utf8(contents)
|
||||
@@ -84,14 +79,13 @@ impl SnapshotMiddleware for SnapshotLua {
|
||||
}))
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
tree: &RbxTree,
|
||||
id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(tree: &RbxTree, id: RbxId) -> SnapshotFileResult {
|
||||
let instance = tree.get_instance(id).unwrap();
|
||||
|
||||
match instance.class_name.as_str() {
|
||||
"ModuleScript" | "LocalScript" | "Script" => unimplemented!("Snapshotting Script instances"),
|
||||
"ModuleScript" | "LocalScript" | "Script" => {
|
||||
unimplemented!("Snapshotting Script instances")
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -126,11 +120,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "ModuleScript");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -145,11 +142,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "Script");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -164,10 +164,13 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "LocalScript");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Source".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
use std::{
|
||||
path::{PathBuf, Path},
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::{
|
||||
new::{Imfs, ImfsEntry, ImfsFetcher, ImfsSnapshot},
|
||||
FsResult,
|
||||
new::{
|
||||
Imfs,
|
||||
ImfsEntry,
|
||||
ImfsFetcher,
|
||||
ImfsSnapshot,
|
||||
},
|
||||
},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
@@ -26,14 +19,9 @@ pub trait SnapshotMiddleware {
|
||||
entry: &ImfsEntry,
|
||||
) -> SnapshotInstanceResult<'static>;
|
||||
|
||||
fn from_instance(
|
||||
tree: &RbxTree,
|
||||
id: RbxId,
|
||||
) -> SnapshotFileResult;
|
||||
fn from_instance(tree: &RbxTree, id: RbxId) -> SnapshotFileResult;
|
||||
|
||||
fn change_affects_paths(
|
||||
path: &Path
|
||||
) -> Vec<PathBuf> {
|
||||
fn change_affects_paths(path: &Path) -> Vec<PathBuf> {
|
||||
vec![path.to_path_buf()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,20 @@ mod rbxm;
|
||||
mod rbxmx;
|
||||
mod txt;
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree};
|
||||
|
||||
use crate::imfs::new::{Imfs, ImfsEntry, ImfsFetcher};
|
||||
use self::{
|
||||
middleware::{SnapshotInstanceResult, SnapshotFileResult, SnapshotMiddleware},
|
||||
csv::SnapshotCsv,
|
||||
dir::SnapshotDir,
|
||||
json_model::SnapshotJsonModel,
|
||||
lua::SnapshotLua,
|
||||
middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware},
|
||||
project::SnapshotProject,
|
||||
rbxm::SnapshotRbxm,
|
||||
rbxmx::SnapshotRbxmx,
|
||||
txt::SnapshotTxt,
|
||||
};
|
||||
use crate::imfs::new::{Imfs, ImfsEntry, ImfsFetcher};
|
||||
|
||||
macro_rules! middlewares {
|
||||
( $($middleware: ident,)* ) => {
|
||||
@@ -73,4 +73,4 @@ middlewares! {
|
||||
SnapshotCsv,
|
||||
SnapshotTxt,
|
||||
SnapshotDir,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree};
|
||||
use rbx_reflection::try_resolve_value;
|
||||
|
||||
use crate::{
|
||||
project::{Project, ProjectNode},
|
||||
imfs::{
|
||||
new::{Imfs, ImfsEntry, ImfsFetcher},
|
||||
FsErrorKind,
|
||||
new::{Imfs, ImfsFetcher, ImfsEntry},
|
||||
},
|
||||
project::{Project, ProjectNode},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware},
|
||||
snapshot_from_imfs,
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
|
||||
pub struct SnapshotProject;
|
||||
@@ -38,7 +35,7 @@ impl SnapshotMiddleware for SnapshotProject {
|
||||
}
|
||||
|
||||
if !entry.path().to_string_lossy().ends_with(".project.json") {
|
||||
return Ok(None)
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let project = Project::load_from_slice(entry.contents(imfs)?, entry.path())
|
||||
@@ -47,10 +44,7 @@ impl SnapshotMiddleware for SnapshotProject {
|
||||
snapshot_project_node(&project.name, &project.tree, imfs)
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
_tree: &RbxTree,
|
||||
_id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(_tree: &RbxTree, _id: RbxId) -> SnapshotFileResult {
|
||||
// TODO: Supporting turning instances into projects
|
||||
None
|
||||
}
|
||||
@@ -61,10 +55,14 @@ fn snapshot_project_node<F: ImfsFetcher>(
|
||||
node: &ProjectNode,
|
||||
imfs: &mut Imfs<F>,
|
||||
) -> SnapshotInstanceResult<'static> {
|
||||
assert!(node.ignore_unknown_instances.is_none(), "TODO: Support $ignoreUnknownInstances");
|
||||
assert!(
|
||||
node.ignore_unknown_instances.is_none(),
|
||||
"TODO: Support $ignoreUnknownInstances"
|
||||
);
|
||||
|
||||
let name = Cow::Owned(instance_name.to_owned());
|
||||
let mut class_name = node.class_name
|
||||
let mut class_name = node
|
||||
.class_name
|
||||
.as_ref()
|
||||
.map(|name| Cow::Owned(name.clone()));
|
||||
let mut properties = HashMap::new();
|
||||
@@ -90,7 +88,7 @@ fn snapshot_project_node<F: ImfsFetcher>(
|
||||
panic!("If $className and $path are specified, $path must yield an instance of class Folder");
|
||||
}
|
||||
}
|
||||
None => Some(snapshot.class_name)
|
||||
None => Some(snapshot.class_name),
|
||||
};
|
||||
|
||||
// Properties from the snapshot are pulled in unchanged, and
|
||||
@@ -108,7 +106,9 @@ fn snapshot_project_node<F: ImfsFetcher>(
|
||||
}
|
||||
} else {
|
||||
// TODO: Should this issue an error instead?
|
||||
log::warn!("$path referred to a path that could not be turned into an instance by Rojo");
|
||||
log::warn!(
|
||||
"$path referred to a path that could not be turned into an instance by Rojo"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ fn snapshot_project_node<F: ImfsFetcher>(
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use rbx_dom_weak::RbxValue;
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::RbxValue;
|
||||
|
||||
use crate::imfs::new::{ImfsSnapshot, NoopFetcher};
|
||||
|
||||
@@ -236,11 +236,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "resolved-properties");
|
||||
assert_eq!(instance_snapshot.class_name, "StringValue");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello, world!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello, world!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
}
|
||||
|
||||
@@ -272,11 +275,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "unresolved-properties");
|
||||
assert_eq!(instance_snapshot.class_name, "StringValue");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hi!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hi!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
}
|
||||
|
||||
@@ -345,11 +351,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "path-project");
|
||||
assert_eq!(instance_snapshot.class_name, "StringValue");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello, world!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello, world!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
}
|
||||
|
||||
@@ -479,11 +488,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "path-property-override");
|
||||
assert_eq!(instance_snapshot.class_name, "StringValue");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Changed".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Changed".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxInstanceProperties, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxInstanceProperties, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{Imfs, ImfsEntry, ImfsFetcher},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotRbxm;
|
||||
|
||||
@@ -25,16 +20,18 @@ impl SnapshotMiddleware for SnapshotRbxm {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_name = entry.path()
|
||||
.file_name().unwrap().to_string_lossy();
|
||||
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
||||
|
||||
if !file_name.ends_with(".rbxm") {
|
||||
return Ok(None);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let instance_name = entry.path()
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
.to_string_lossy().to_string();
|
||||
let instance_name = entry
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("Could not extract file stem")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let mut temp_tree = RbxTree::new(RbxInstanceProperties {
|
||||
name: "DataModel".to_owned(),
|
||||
@@ -59,10 +56,7 @@ impl SnapshotMiddleware for SnapshotRbxm {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
_tree: &RbxTree,
|
||||
_id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(_tree: &RbxTree, _id: RbxId) -> SnapshotFileResult {
|
||||
unimplemented!("Snapshotting models");
|
||||
}
|
||||
}
|
||||
@@ -93,4 +87,4 @@ mod test {
|
||||
// property that currently deserializes incorrectly.
|
||||
// See: https://github.com/rojo-rbx/rbx-dom/issues/49
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rbx_dom_weak::{RbxTree, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{Imfs, ImfsEntry, ImfsFetcher},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotRbxmx;
|
||||
|
||||
@@ -22,16 +20,18 @@ impl SnapshotMiddleware for SnapshotRbxmx {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_name = entry.path()
|
||||
.file_name().unwrap().to_string_lossy();
|
||||
let file_name = entry.path().file_name().unwrap().to_string_lossy();
|
||||
|
||||
if !file_name.ends_with(".rbxmx") {
|
||||
return Ok(None);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let instance_name = entry.path()
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
.to_string_lossy().to_string();
|
||||
let instance_name = entry
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("Could not extract file stem")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let options = rbx_xml::DecodeOptions::new()
|
||||
.property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown);
|
||||
@@ -52,10 +52,7 @@ impl SnapshotMiddleware for SnapshotRbxmx {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
_tree: &RbxTree,
|
||||
_id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(_tree: &RbxTree, _id: RbxId) -> SnapshotFileResult {
|
||||
unimplemented!("Snapshotting models");
|
||||
}
|
||||
}
|
||||
@@ -71,7 +68,8 @@ mod test {
|
||||
#[test]
|
||||
fn model_from_imfs() {
|
||||
let mut imfs = Imfs::new(NoopFetcher);
|
||||
let file = ImfsSnapshot::file(r#"
|
||||
let file = ImfsSnapshot::file(
|
||||
r#"
|
||||
<roblox version="4">
|
||||
<Item class="Folder" referent="0">
|
||||
<Properties>
|
||||
@@ -79,16 +77,19 @@ mod test {
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
|
||||
imfs.load_from_snapshot("/foo.rbxmx", file);
|
||||
|
||||
let entry = imfs.get("/foo.rbxmx").unwrap();
|
||||
let instance_snapshot = SnapshotRbxmx::from_imfs(&mut imfs, &entry).unwrap().unwrap();
|
||||
let instance_snapshot = SnapshotRbxmx::from_imfs(&mut imfs, &entry)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "Folder");
|
||||
assert_eq!(instance_snapshot.properties, HashMap::new());
|
||||
assert_eq!(instance_snapshot.children, Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
str,
|
||||
};
|
||||
use std::{borrow::Cow, str};
|
||||
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::{RbxTree, RbxValue, RbxId};
|
||||
use rbx_dom_weak::{RbxId, RbxTree, RbxValue};
|
||||
|
||||
use crate::{
|
||||
imfs::new::{Imfs, ImfsSnapshot, FileSnapshot, ImfsFetcher, ImfsEntry},
|
||||
imfs::new::{FileSnapshot, Imfs, ImfsEntry, ImfsFetcher, ImfsSnapshot},
|
||||
snapshot::InstanceSnapshot,
|
||||
};
|
||||
|
||||
use super::{
|
||||
middleware::{SnapshotMiddleware, SnapshotInstanceResult, SnapshotFileResult},
|
||||
};
|
||||
use super::middleware::{SnapshotFileResult, SnapshotInstanceResult, SnapshotMiddleware};
|
||||
|
||||
pub struct SnapshotTxt;
|
||||
|
||||
@@ -35,13 +30,18 @@ impl SnapshotMiddleware for SnapshotTxt {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let instance_name = entry.path()
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
.to_str().unwrap().to_string();
|
||||
let instance_name = entry
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("Could not extract file stem")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let contents = entry.contents(imfs)?;
|
||||
let contents_str = str::from_utf8(contents)
|
||||
.expect("File content was not valid UTF-8").to_string();
|
||||
.expect("File content was not valid UTF-8")
|
||||
.to_string();
|
||||
|
||||
let properties = hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
@@ -58,10 +58,7 @@ impl SnapshotMiddleware for SnapshotTxt {
|
||||
}))
|
||||
}
|
||||
|
||||
fn from_instance(
|
||||
tree: &RbxTree,
|
||||
id: RbxId,
|
||||
) -> SnapshotFileResult {
|
||||
fn from_instance(tree: &RbxTree, id: RbxId) -> SnapshotFileResult {
|
||||
let instance = tree.get_instance(id).unwrap();
|
||||
|
||||
if instance.class_name != "StringValue" {
|
||||
@@ -94,7 +91,7 @@ mod test {
|
||||
use super::*;
|
||||
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::{RbxInstanceProperties};
|
||||
use rbx_dom_weak::RbxInstanceProperties;
|
||||
|
||||
use crate::imfs::new::NoopFetcher;
|
||||
|
||||
@@ -110,11 +107,14 @@ mod test {
|
||||
|
||||
assert_eq!(instance_snapshot.name, "foo");
|
||||
assert_eq!(instance_snapshot.class_name, "StringValue");
|
||||
assert_eq!(instance_snapshot.properties, hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
});
|
||||
assert_eq!(
|
||||
instance_snapshot.properties,
|
||||
hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
value: "Hello there!".to_owned(),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -144,4 +144,4 @@ mod test {
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,15 @@
|
||||
//! Defines Rojo's HTTP API, all under /api. These endpoints generally return
|
||||
//! JSON.
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
use futures::{
|
||||
future,
|
||||
Future,
|
||||
};
|
||||
use futures::{future, Future};
|
||||
|
||||
use hyper::{
|
||||
service::Service,
|
||||
header,
|
||||
StatusCode,
|
||||
Method,
|
||||
Body,
|
||||
Request,
|
||||
Response,
|
||||
};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode};
|
||||
use rbx_dom_weak::RbxId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
serve_session::ServeSession,
|
||||
session_id::SessionId,
|
||||
};
|
||||
use crate::{serve_session::ServeSession, session_id::SessionId};
|
||||
|
||||
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const PROTOCOL_VERSION: u64 = 3;
|
||||
@@ -83,7 +66,8 @@ impl Service for ApiService {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
type Error = hyper::Error;
|
||||
type Future = Box<dyn Future<Item = hyper::Response<Self::ReqBody>, Error = Self::Error> + Send>;
|
||||
type Future =
|
||||
Box<dyn Future<Item = hyper::Response<Self::ReqBody>, Error = Self::Error> + Send>;
|
||||
|
||||
fn call(&mut self, request: hyper::Request<Self::ReqBody>) -> Self::Future {
|
||||
let response = match (request.method(), request.uri().path()) {
|
||||
@@ -92,12 +76,10 @@ impl Service for ApiService {
|
||||
(&Method::GET, path) if path.starts_with("/api/subscribe/") => {
|
||||
return self.handle_api_subscribe(request);
|
||||
}
|
||||
_ => {
|
||||
Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::empty())
|
||||
.unwrap()
|
||||
}
|
||||
_ => Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
Box::new(future::ok(response))
|
||||
@@ -106,9 +88,7 @@ impl Service for ApiService {
|
||||
|
||||
impl ApiService {
|
||||
pub fn new(serve_session: Arc<ServeSession>) -> ApiService {
|
||||
ApiService {
|
||||
serve_session,
|
||||
}
|
||||
ApiService { serve_session }
|
||||
}
|
||||
|
||||
/// Get a summary of information about the server
|
||||
@@ -128,12 +108,14 @@ impl ApiService {
|
||||
let _cursor: u32 = match argument.parse() {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return Box::new(future::ok(Response::builder()
|
||||
.status(StatusCode::BAD_REQUEST)
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body(Body::from(err.to_string()))
|
||||
.unwrap()));
|
||||
},
|
||||
return Box::new(future::ok(
|
||||
Response::builder()
|
||||
.status(StatusCode::BAD_REQUEST)
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body(Body::from(err.to_string()))
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(future::ok(response_json(SubscribeResponse {
|
||||
@@ -143,10 +125,7 @@ impl ApiService {
|
||||
|
||||
fn handle_api_read(&self, request: Request<Body>) -> Response<Body> {
|
||||
let argument = &request.uri().path()["/api/read/".len()..];
|
||||
let requested_ids: Option<Vec<RbxId>> = argument
|
||||
.split(',')
|
||||
.map(RbxId::parse_str)
|
||||
.collect();
|
||||
let requested_ids: Option<Vec<RbxId>> = argument.split(',').map(RbxId::parse_str).collect();
|
||||
|
||||
let _requested_ids = match requested_ids {
|
||||
Some(id) => id,
|
||||
@@ -156,11 +135,11 @@ impl ApiService {
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body(Body::from("Malformed ID list"))
|
||||
.unwrap();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
response_json(ReadResponse {
|
||||
session_id: self.serve_session.session_id(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{future, Future};
|
||||
use hyper::{
|
||||
service::Service,
|
||||
header,
|
||||
Body,
|
||||
Method,
|
||||
StatusCode,
|
||||
Request,
|
||||
Response,
|
||||
};
|
||||
use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode};
|
||||
use ritz::html;
|
||||
|
||||
use crate::serve_session::ServeSession;
|
||||
@@ -47,9 +39,7 @@ impl Service for InterfaceService {
|
||||
|
||||
impl InterfaceService {
|
||||
pub fn new(serve_session: Arc<ServeSession>) -> InterfaceService {
|
||||
InterfaceService {
|
||||
serve_session,
|
||||
}
|
||||
InterfaceService { serve_session }
|
||||
}
|
||||
|
||||
fn handle_home(&self) -> Response<Body> {
|
||||
@@ -97,4 +87,4 @@ impl InterfaceService {
|
||||
.body(Body::from("TODO: /visualize/imfs"))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,16 @@ mod interface;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::trace;
|
||||
use futures::{
|
||||
future::{self, FutureResult},
|
||||
Future,
|
||||
};
|
||||
use hyper::{
|
||||
service::Service,
|
||||
Body,
|
||||
Request,
|
||||
Response,
|
||||
Server,
|
||||
};
|
||||
use hyper::{service::Service, Body, Request, Response, Server};
|
||||
use log::trace;
|
||||
|
||||
use crate::serve_session::ServeSession;
|
||||
|
||||
use self::{
|
||||
api::ApiService,
|
||||
interface::InterfaceService,
|
||||
};
|
||||
use self::{api::ApiService, interface::InterfaceService};
|
||||
|
||||
pub struct RootService {
|
||||
api: api::ApiService,
|
||||
@@ -60,9 +51,7 @@ pub struct LiveServer {
|
||||
|
||||
impl LiveServer {
|
||||
pub fn new(serve_session: Arc<ServeSession>) -> LiveServer {
|
||||
LiveServer {
|
||||
serve_session,
|
||||
}
|
||||
LiveServer { serve_session }
|
||||
}
|
||||
|
||||
pub fn start(self, port: u16) {
|
||||
@@ -78,4 +67,4 @@ impl LiveServer {
|
||||
|
||||
hyper::rt::run(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, BTreeMap},
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use rbx_dom_weak::RbxValue;
|
||||
|
||||
use librojo::{
|
||||
project::{Project, ProjectNode},
|
||||
};
|
||||
use librojo::project::{Project, ProjectNode};
|
||||
|
||||
lazy_static! {
|
||||
static ref TEST_PROJECTS_ROOT: PathBuf = {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../test-projects")
|
||||
};
|
||||
static ref TEST_PROJECTS_ROOT: PathBuf =
|
||||
{ Path::new(env!("CARGO_MANIFEST_DIR")).join("../test-projects") };
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -63,9 +61,10 @@ fn single_partition_game() {
|
||||
};
|
||||
|
||||
let mut http_service_properties = HashMap::new();
|
||||
http_service_properties.insert("HttpEnabled".to_string(), RbxValue::Bool {
|
||||
value: true,
|
||||
}.into());
|
||||
http_service_properties.insert(
|
||||
"HttpEnabled".to_string(),
|
||||
RbxValue::Bool { value: true }.into(),
|
||||
);
|
||||
|
||||
let http_service = ProjectNode {
|
||||
class_name: Some(String::from("HttpService")),
|
||||
@@ -110,4 +109,4 @@ fn composing_models() {
|
||||
let project = Project::load_fuzzy(&project_file_location).unwrap();
|
||||
|
||||
assert_eq!(project.name, "composing-models");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user