diff --git a/server/src/lib.rs b/server/src/lib.rs index d15c4d68..6b021803 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -7,7 +7,6 @@ extern crate serde_derive; #[cfg(test)] extern crate tempfile; -// pub mod roblox_studio; pub mod commands; pub mod fs_watcher; pub mod imfs; diff --git a/server/src/message_queue.rs b/server/src/message_queue.rs index dd86bc04..9eefb749 100644 --- a/server/src/message_queue.rs +++ b/server/src/message_queue.rs @@ -19,6 +19,10 @@ pub fn get_listener_id() -> ListenerId { ListenerId(LAST_ID.fetch_add(1, Ordering::SeqCst)) } +/// A message queue with persistent history that can be subscribed to. +/// +/// Definitely non-optimal, but a simple design that works well for the +/// synchronous web server Rojo uses, Rouille. #[derive(Default)] pub struct MessageQueue { messages: RwLock>, diff --git a/server/src/path_map.rs b/server/src/path_map.rs index b32302e2..a62e2c55 100644 --- a/server/src/path_map.rs +++ b/server/src/path_map.rs @@ -9,8 +9,9 @@ struct PathMapNode { children: HashSet, } -/// A map from paths to instance IDs, with a bit of additional data that enables -/// removing a path and all of its child paths from the tree more quickly. +/// A map from paths to another type, like instance IDs, with a bit of +/// additional data that enables removing a path and all of its child paths from +/// the tree more quickly. #[derive(Debug, Serialize)] pub struct PathMap { nodes: HashMap>, @@ -71,6 +72,14 @@ impl PathMap { Some(root_value) } + /// Traverses the route between `start_path` and `target_path` and returns + /// the path closest to `target_path` in the tree. + /// + /// This is useful when trying to determine what paths need to be marked as + /// altered when a change to a path is registered. Depending on the order of + /// 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: &Path, target_path: &Path) -> PathBuf { let relative_path = target_path.strip_prefix(start_path) .expect("target_path did not begin with start_path"); diff --git a/server/src/project.rs b/server/src/project.rs index 6cfd147f..95b71402 100644 --- a/server/src/project.rs +++ b/server/src/project.rs @@ -12,7 +12,8 @@ use rbx_tree::RbxValue; pub static PROJECT_FILENAME: &'static str = "roblox-project.json"; -// Serde is silly. +// Methods used for Serde's default value system, which doesn't support using +// value literals directly, only functions that return values. const fn yeah() -> bool { true } @@ -21,6 +22,40 @@ const fn is_true(value: &bool) -> bool { *value } +/// SourceProject is the format that users author projects on-disk. Since we +/// want to do things like transforming paths to be absolute before handing them +/// off to the rest of Rojo, we use this intermediate struct. +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourceProject { + name: String, + tree: SourceProjectNode, + + #[serde(skip_serializing_if = "Option::is_none")] + serve_port: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + serve_place_ids: Option>, +} + +impl SourceProject { + /// Consumes the SourceProject and yields a Project, ready for prime-time. + pub fn into_project(self, project_file_location: &Path) -> Project { + let tree = self.tree.into_project_node(project_file_location); + + Project { + name: self.name, + tree, + serve_port: self.serve_port, + serve_place_ids: self.serve_place_ids, + file_location: PathBuf::from(project_file_location), + } + } +} + +/// Similar to SourceProject, the structure of nodes in the project tree is +/// slightly different on-disk than how we want to handle them in the rest of +/// Rojo. #[derive(Debug, Serialize, Deserialize)] #[serde(untagged)] enum SourceProjectNode { @@ -44,6 +79,7 @@ enum SourceProjectNode { } impl SourceProjectNode { + /// Consumes the SourceProjectNode and turns it into a ProjectNode. pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode { match self { SourceProjectNode::Instance { class_name, mut children, properties, ignore_unknown_instances } => { @@ -78,31 +114,7 @@ impl SourceProjectNode { } } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SourceProject { - name: String, - tree: SourceProjectNode, - #[serde(skip_serializing_if = "Option::is_none")] - serve_port: Option, - #[serde(skip_serializing_if = "Option::is_none")] - serve_place_ids: Option>, -} - -impl SourceProject { - pub fn into_project(self, project_file_location: &Path) -> Project { - let tree = self.tree.into_project_node(project_file_location); - - Project { - name: self.name, - tree, - serve_port: self.serve_port, - serve_place_ids: self.serve_place_ids, - file_location: PathBuf::from(project_file_location), - } - } -} - +/// Error returned by Project::load_exact #[derive(Debug, Fail)] pub enum ProjectLoadExactError { #[fail(display = "IO error: {}", _0)] @@ -112,6 +124,7 @@ pub enum ProjectLoadExactError { JsonError(#[fail(cause)] serde_json::Error), } +/// Error returned by Project::load_fuzzy #[derive(Debug, Fail)] pub enum ProjectLoadFuzzyError { #[fail(display = "Project not found")] @@ -133,6 +146,7 @@ impl From for ProjectLoadFuzzyError { } } +/// Error returned by Project::init_place and Project::init_model #[derive(Debug, Fail)] pub enum ProjectInitError { AlreadyExists(PathBuf), @@ -150,6 +164,7 @@ impl fmt::Display for ProjectInitError { } } +/// Error returned by Project::save #[derive(Debug, Fail)] pub enum ProjectSaveError { #[fail(display = "JSON error: {}", _0)] @@ -340,7 +355,7 @@ impl Project { // TODO: Check for specific error kinds, convert 'not found' to Result. let location_metadata = fs::metadata(start_location).ok()?; - // If this is a file, we should assume it's the config we want + // If this is a file, assume it's the config the user was looking for. if location_metadata.is_file() { return Some(start_location.to_path_buf()); } else if location_metadata.is_dir() { diff --git a/server/src/roblox_studio.rs b/server/src/roblox_studio.rs deleted file mode 100644 index 195e86be..00000000 --- a/server/src/roblox_studio.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Interactions with Roblox Studio's installation, including its location and -//! mechanisms like PluginSettings. - -#![allow(dead_code)] - -use std::path::PathBuf; - -#[cfg(all(not(debug_assertions), not(feature = "bundle-plugin")))] -compile_error!("`bundle-plugin` feature must be set for release builds."); - -#[cfg(feature = "bundle-plugin")] -static PLUGIN_RBXM: &'static [u8] = include_bytes!("../target/plugin.rbxmx"); - -#[cfg(target_os = "windows")] -pub fn get_install_location() -> Option { - use std::env; - - let local_app_data = env::var("LocalAppData").ok()?; - let mut location = PathBuf::from(local_app_data); - - location.push("Roblox"); - - Some(location) -} - -#[cfg(target_os = "macos")] -pub fn get_install_location() -> Option { - unimplemented!(); -} - -#[cfg(not(any(target_os = "windows", target_os = "macos")))] -pub fn get_install_location() -> Option { - // Roblox Studio doesn't install on any other platforms! - None -} - -pub fn get_plugin_location() -> Option { - let mut location = get_install_location()?; - - location.push("Plugins/Rojo.rbxmx"); - - Some(location) -} - -#[cfg(feature = "bundle-plugin")] -pub fn install_bundled_plugin() -> Option<()> { - use std::fs::File; - use std::io::Write; - - info!("Installing plugin..."); - - let mut file = File::create(get_plugin_location()?).ok()?; - file.write_all(PLUGIN_RBXM).ok()?; - - Some(()) -} - -#[cfg(not(feature = "bundle-plugin"))] -pub fn install_bundled_plugin() -> Option<()> { - info!("Skipping plugin installation, bundle-plugin not set."); - - Some(()) -} \ No newline at end of file diff --git a/server/src/visualize.rs b/server/src/visualize.rs index 0a39212e..22f91591 100644 --- a/server/src/visualize.rs +++ b/server/src/visualize.rs @@ -25,6 +25,7 @@ digraph RojoTree { ]; "#; +/// Compiles DOT source to SVG by invoking dot on the command line. pub fn graphviz_to_svg(source: &str) -> String { let mut child = Command::new("dot") .arg("-Tsvg") @@ -42,6 +43,7 @@ pub fn graphviz_to_svg(source: &str) -> String { String::from_utf8(output.stdout).expect("Failed to parse stdout as UTF-8") } +/// A Display wrapper struct to visualize an RbxSession as SVG. pub struct VisualizeRbxSession<'a>(pub &'a RbxSession); impl<'a> fmt::Display for VisualizeRbxSession<'a> { @@ -81,6 +83,7 @@ fn visualize_rbx_node(session: &RbxSession, id: RbxId, output: &mut fmt::Formatt Ok(()) } +/// A Display wrapper struct to visualize an Imfs as SVG. pub struct VisualizeImfs<'a>(pub &'a Imfs); impl<'a> fmt::Display for VisualizeImfs<'a> {