mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-24 14:45:56 +00:00
Clean up and document code throughout the server
This commit is contained in:
@@ -7,7 +7,6 @@ extern crate serde_derive;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
|
|
||||||
// pub mod roblox_studio;
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod fs_watcher;
|
pub mod fs_watcher;
|
||||||
pub mod imfs;
|
pub mod imfs;
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ pub fn get_listener_id() -> ListenerId {
|
|||||||
ListenerId(LAST_ID.fetch_add(1, Ordering::SeqCst))
|
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)]
|
#[derive(Default)]
|
||||||
pub struct MessageQueue<T> {
|
pub struct MessageQueue<T> {
|
||||||
messages: RwLock<Vec<T>>,
|
messages: RwLock<Vec<T>>,
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ struct PathMapNode<T> {
|
|||||||
children: HashSet<PathBuf>,
|
children: HashSet<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A map from paths to instance IDs, with a bit of additional data that enables
|
/// A map from paths to another type, like instance IDs, with a bit of
|
||||||
/// removing a path and all of its child paths from the tree more quickly.
|
/// additional data that enables removing a path and all of its child paths from
|
||||||
|
/// the tree more quickly.
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct PathMap<T> {
|
pub struct PathMap<T> {
|
||||||
nodes: HashMap<PathBuf, PathMapNode<T>>,
|
nodes: HashMap<PathBuf, PathMapNode<T>>,
|
||||||
@@ -71,6 +72,14 @@ impl<T> PathMap<T> {
|
|||||||
Some(root_value)
|
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 {
|
pub fn descend(&self, start_path: &Path, target_path: &Path) -> PathBuf {
|
||||||
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");
|
.expect("target_path did not begin with start_path");
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ use rbx_tree::RbxValue;
|
|||||||
|
|
||||||
pub static PROJECT_FILENAME: &'static str = "roblox-project.json";
|
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 {
|
const fn yeah() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,40 @@ const fn is_true(value: &bool) -> bool {
|
|||||||
*value
|
*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<u16>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
serve_place_ids: Option<HashSet<u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum SourceProjectNode {
|
enum SourceProjectNode {
|
||||||
@@ -44,6 +79,7 @@ enum SourceProjectNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SourceProjectNode {
|
impl SourceProjectNode {
|
||||||
|
/// Consumes the SourceProjectNode and turns it into a ProjectNode.
|
||||||
pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode {
|
pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode {
|
||||||
match self {
|
match self {
|
||||||
SourceProjectNode::Instance { class_name, mut children, properties, ignore_unknown_instances } => {
|
SourceProjectNode::Instance { class_name, mut children, properties, ignore_unknown_instances } => {
|
||||||
@@ -78,31 +114,7 @@ impl SourceProjectNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
/// Error returned by Project::load_exact
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct SourceProject {
|
|
||||||
name: String,
|
|
||||||
tree: SourceProjectNode,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
serve_port: Option<u16>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
serve_place_ids: Option<HashSet<u64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ProjectLoadExactError {
|
pub enum ProjectLoadExactError {
|
||||||
#[fail(display = "IO error: {}", _0)]
|
#[fail(display = "IO error: {}", _0)]
|
||||||
@@ -112,6 +124,7 @@ pub enum ProjectLoadExactError {
|
|||||||
JsonError(#[fail(cause)] serde_json::Error),
|
JsonError(#[fail(cause)] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error returned by Project::load_fuzzy
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ProjectLoadFuzzyError {
|
pub enum ProjectLoadFuzzyError {
|
||||||
#[fail(display = "Project not found")]
|
#[fail(display = "Project not found")]
|
||||||
@@ -133,6 +146,7 @@ impl From<ProjectLoadExactError> for ProjectLoadFuzzyError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error returned by Project::init_place and Project::init_model
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ProjectInitError {
|
pub enum ProjectInitError {
|
||||||
AlreadyExists(PathBuf),
|
AlreadyExists(PathBuf),
|
||||||
@@ -150,6 +164,7 @@ impl fmt::Display for ProjectInitError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error returned by Project::save
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ProjectSaveError {
|
pub enum ProjectSaveError {
|
||||||
#[fail(display = "JSON error: {}", _0)]
|
#[fail(display = "JSON error: {}", _0)]
|
||||||
@@ -340,7 +355,7 @@ impl Project {
|
|||||||
// TODO: Check for specific error kinds, convert 'not found' to Result.
|
// TODO: Check for specific error kinds, convert 'not found' to Result.
|
||||||
let location_metadata = fs::metadata(start_location).ok()?;
|
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() {
|
if location_metadata.is_file() {
|
||||||
return Some(start_location.to_path_buf());
|
return Some(start_location.to_path_buf());
|
||||||
} else if location_metadata.is_dir() {
|
} else if location_metadata.is_dir() {
|
||||||
|
|||||||
@@ -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<PathBuf> {
|
|
||||||
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<PathBuf> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
|
||||||
pub fn get_install_location() -> Option<PathBuf> {
|
|
||||||
// Roblox Studio doesn't install on any other platforms!
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_plugin_location() -> Option<PathBuf> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
@@ -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 {
|
pub fn graphviz_to_svg(source: &str) -> String {
|
||||||
let mut child = Command::new("dot")
|
let mut child = Command::new("dot")
|
||||||
.arg("-Tsvg")
|
.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")
|
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);
|
pub struct VisualizeRbxSession<'a>(pub &'a RbxSession);
|
||||||
|
|
||||||
impl<'a> fmt::Display for VisualizeRbxSession<'a> {
|
impl<'a> fmt::Display for VisualizeRbxSession<'a> {
|
||||||
@@ -81,6 +83,7 @@ fn visualize_rbx_node(session: &RbxSession, id: RbxId, output: &mut fmt::Formatt
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Display wrapper struct to visualize an Imfs as SVG.
|
||||||
pub struct VisualizeImfs<'a>(pub &'a Imfs);
|
pub struct VisualizeImfs<'a>(pub &'a Imfs);
|
||||||
|
|
||||||
impl<'a> fmt::Display for VisualizeImfs<'a> {
|
impl<'a> fmt::Display for VisualizeImfs<'a> {
|
||||||
|
|||||||
Reference in New Issue
Block a user