forked from rojo-rbx/rojo
Merge branch 'serve-instances'
This commit is contained in:
76
Cargo.lock
generated
76
Cargo.lock
generated
@@ -6,6 +6,14 @@ dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.10.2"
|
||||
@@ -269,6 +277,11 @@ dependencies = [
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.34"
|
||||
@@ -292,6 +305,14 @@ dependencies = [
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.1.3"
|
||||
@@ -510,18 +531,37 @@ dependencies = [
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rojo"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rouille 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -694,6 +734,15 @@ dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.38"
|
||||
@@ -742,6 +791,14 @@ name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unreachable"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "0.2.38"
|
||||
@@ -767,6 +824,11 @@ name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.1.18"
|
||||
@@ -781,6 +843,11 @@ name = "vec_map"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.0.1"
|
||||
@@ -810,6 +877,7 @@ dependencies = [
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||
"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50"
|
||||
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
|
||||
@@ -845,10 +913,12 @@ dependencies = [
|
||||
"checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c"
|
||||
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
||||
"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
|
||||
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
|
||||
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ec0c2f4d901bf1d4a2192a40b4b570ae3b19c51243e549defc1de741940aa787"
|
||||
"checksum mime_guess 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "422acd80644209a8c8c66a20514840d8c092eb1eab2898ca7c548cc1d64c8998"
|
||||
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
|
||||
@@ -872,7 +942,9 @@ dependencies = [
|
||||
"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ac6ab4e9218ade5b423358bbd2567d1617418403c7a512603630181813316322"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum rouille 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6415f261a8775bef50e9fcfb14ed73209ce637f753f9d1c8c6122559e559001"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum same-file 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a18720d745fb9ca6a041b37cb36d0b21066006b6cff8b5b360142d4b81fb60"
|
||||
@@ -893,17 +965,21 @@ dependencies = [
|
||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||
"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
|
||||
"checksum tiny_http 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "016f040cfc9b5be610de3619eaaa57017fa0b0b678187327bde75fc146e2a41f"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
|
||||
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068"
|
||||
"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
|
||||
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b6d201f4f8998a837196b6de9c73e35af14c992cbb92c4ab641d2c2dce52de"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
@@ -18,3 +18,5 @@ serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
notify = "4.0.0"
|
||||
rand = "0.3"
|
||||
regex = "0.2"
|
||||
lazy_static = "1.0"
|
||||
|
||||
20
DESIGN.md
Normal file
20
DESIGN.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Rojo Design
|
||||
This is a super rough draft that I'm trying to use to lay out of my thoughts.
|
||||
|
||||
## API
|
||||
|
||||
### POST `/read`
|
||||
Accepts a `Vec<Route>` of items to read.
|
||||
|
||||
Returns `Vec<Option<RbxItem>>`, in the same order as the request.
|
||||
|
||||
### POST `/write`
|
||||
Accepts a `Vec<{ Route, RbxItem }>` of items to write.
|
||||
|
||||
I imagine that the `Name` attribute of the top-level `RbxItem` would be ignored in favor of the route name?
|
||||
|
||||
## CLI
|
||||
|
||||
### Transform Plugins
|
||||
|
||||
## Roblox Studio Plugin
|
||||
24
src/bin.rs
24
src/bin.rs
@@ -7,10 +7,14 @@ extern crate rouille;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate notify;
|
||||
extern crate rand;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate regex;
|
||||
|
||||
pub mod web;
|
||||
pub mod core;
|
||||
@@ -18,6 +22,9 @@ pub mod project;
|
||||
pub mod pathext;
|
||||
pub mod vfs;
|
||||
pub mod vfs_watch;
|
||||
pub mod rbx;
|
||||
pub mod plugin;
|
||||
pub mod plugins;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -26,6 +33,8 @@ use std::thread;
|
||||
use core::Config;
|
||||
use pathext::canonicalish;
|
||||
use project::{Project, ProjectLoadError};
|
||||
use plugin::{PluginChain};
|
||||
use plugins::{DefaultPlugin, ScriptPlugin};
|
||||
use vfs::Vfs;
|
||||
use vfs_watch::VfsWatcher;
|
||||
|
||||
@@ -141,6 +150,13 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref PLUGIN_CHAIN: PluginChain = PluginChain::new(vec![
|
||||
Box::new(ScriptPlugin::new()),
|
||||
Box::new(DefaultPlugin::new()),
|
||||
]);
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
port,
|
||||
verbose,
|
||||
@@ -152,7 +168,7 @@ fn main() {
|
||||
}
|
||||
|
||||
let vfs = {
|
||||
let mut vfs = Vfs::new(config.clone());
|
||||
let mut vfs = Vfs::new(config.clone(), &PLUGIN_CHAIN);
|
||||
|
||||
for (name, project_partition) in &project.partitions {
|
||||
let path = {
|
||||
@@ -189,13 +205,9 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
web::start(config.clone(), project.clone(), vfs.clone());
|
||||
|
||||
println!("Server listening on port {}", port);
|
||||
|
||||
loop {
|
||||
thread::park();
|
||||
}
|
||||
web::start(config.clone(), project.clone(), &PLUGIN_CHAIN, vfs.clone());
|
||||
},
|
||||
("pack", _) => {
|
||||
eprintln!("'rojo pack' is not yet implemented!");
|
||||
|
||||
@@ -4,3 +4,5 @@ pub struct Config {
|
||||
pub verbose: bool,
|
||||
pub server_id: u64,
|
||||
}
|
||||
|
||||
pub type Route = Vec<String>;
|
||||
|
||||
69
src/plugin.rs
Normal file
69
src/plugin.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use rbx::RbxItem;
|
||||
use vfs::VfsItem;
|
||||
use core::Route;
|
||||
|
||||
pub enum TransformFileResult {
|
||||
Value(Option<RbxItem>),
|
||||
Pass,
|
||||
}
|
||||
|
||||
pub enum RbxChangeResult {
|
||||
Write(Option<VfsItem>),
|
||||
Pass,
|
||||
}
|
||||
|
||||
pub enum FileChangeResult {
|
||||
MarkChanged(Option<Vec<Route>>),
|
||||
Pass,
|
||||
}
|
||||
|
||||
pub trait Plugin {
|
||||
fn transform_file(&self, plugins: &PluginChain, vfs_item: &VfsItem) -> TransformFileResult;
|
||||
fn handle_rbx_change(&self, route: &Route, rbx_item: &RbxItem) -> RbxChangeResult;
|
||||
fn handle_file_change(&self, route: &Route) -> FileChangeResult;
|
||||
}
|
||||
|
||||
pub struct PluginChain {
|
||||
plugins: Vec<Box<Plugin + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl PluginChain {
|
||||
pub fn new(plugins: Vec<Box<Plugin + Send + Sync>>) -> PluginChain {
|
||||
PluginChain {
|
||||
plugins,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_file(&self, vfs_item: &VfsItem) -> Option<RbxItem> {
|
||||
for plugin in &self.plugins {
|
||||
match plugin.transform_file(self, vfs_item) {
|
||||
TransformFileResult::Value(rbx_item) => return rbx_item,
|
||||
TransformFileResult::Pass => {},
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn handle_rbx_change(&self, route: &Route, rbx_item: &RbxItem) -> Option<VfsItem> {
|
||||
for plugin in &self.plugins {
|
||||
match plugin.handle_rbx_change(route, rbx_item) {
|
||||
RbxChangeResult::Write(vfs_item) => return vfs_item,
|
||||
RbxChangeResult::Pass => {},
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn handle_file_change(&self, route: &Route) -> Option<Vec<Route>> {
|
||||
for plugin in &self.plugins {
|
||||
match plugin.handle_file_change(route) {
|
||||
FileChangeResult::MarkChanged(changes) => return changes,
|
||||
FileChangeResult::Pass => {},
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
65
src/plugins/default_plugin.rs
Normal file
65
src/plugins/default_plugin.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use core::Route;
|
||||
use plugin::{Plugin, PluginChain, TransformFileResult, RbxChangeResult, FileChangeResult};
|
||||
use rbx::{RbxItem, RbxValue};
|
||||
use vfs::VfsItem;
|
||||
|
||||
/// A plugin with simple transforms:
|
||||
/// * Directories become Folder instances
|
||||
/// * Files become StringValue objects with 'Value' as their contents
|
||||
pub struct DefaultPlugin;
|
||||
|
||||
impl DefaultPlugin {
|
||||
pub fn new() -> DefaultPlugin {
|
||||
DefaultPlugin
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for DefaultPlugin {
|
||||
fn transform_file(&self, plugins: &PluginChain, vfs_item: &VfsItem) -> TransformFileResult {
|
||||
match vfs_item {
|
||||
&VfsItem::File { ref contents, ref name } => {
|
||||
let mut properties = HashMap::new();
|
||||
|
||||
properties.insert("Value".to_string(), RbxValue::String {
|
||||
value: contents.clone(),
|
||||
});
|
||||
|
||||
TransformFileResult::Value(Some(RbxItem {
|
||||
name: name.clone(),
|
||||
class_name: "StringValue".to_string(),
|
||||
children: Vec::new(),
|
||||
properties,
|
||||
}))
|
||||
},
|
||||
&VfsItem::Dir { ref children, ref name } => {
|
||||
let mut rbx_children = Vec::new();
|
||||
|
||||
for (_, child_item) in children {
|
||||
match plugins.transform_file(child_item) {
|
||||
Some(rbx_item) => {
|
||||
rbx_children.push(rbx_item);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
TransformFileResult::Value(Some(RbxItem {
|
||||
name: name.clone(),
|
||||
class_name: "Folder".to_string(),
|
||||
children: rbx_children,
|
||||
properties: HashMap::new(),
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_file_change(&self, route: &Route) -> FileChangeResult {
|
||||
FileChangeResult::MarkChanged(Some(vec![route.clone()]))
|
||||
}
|
||||
|
||||
fn handle_rbx_change(&self, route: &Route, rbx_item: &RbxItem) -> RbxChangeResult {
|
||||
RbxChangeResult::Pass
|
||||
}
|
||||
}
|
||||
5
src/plugins/mod.rs
Normal file
5
src/plugins/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod default_plugin;
|
||||
mod script_plugin;
|
||||
|
||||
pub use self::default_plugin::*;
|
||||
pub use self::script_plugin::*;
|
||||
121
src/plugins/script_plugin.rs
Normal file
121
src/plugins/script_plugin.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use core::Route;
|
||||
use plugin::{Plugin, PluginChain, TransformFileResult, RbxChangeResult, FileChangeResult};
|
||||
use rbx::{RbxItem, RbxValue};
|
||||
use vfs::VfsItem;
|
||||
|
||||
lazy_static! {
|
||||
static ref SERVER_PATTERN: Regex = Regex::new(r"^(.*?)\.server\.lua$").unwrap();
|
||||
static ref CLIENT_PATTERN: Regex = Regex::new(r"^(.*?)\.client\.lua$").unwrap();
|
||||
static ref MODULE_PATTERN: Regex = Regex::new(r"^(.*?)\.lua$").unwrap();
|
||||
}
|
||||
|
||||
static SERVER_INIT: &'static str = "init.server.lua";
|
||||
static CLIENT_INIT: &'static str = "init.client.lua";
|
||||
static MODULE_INIT: &'static str = "init.lua";
|
||||
|
||||
pub struct ScriptPlugin;
|
||||
|
||||
impl ScriptPlugin {
|
||||
pub fn new() -> ScriptPlugin {
|
||||
ScriptPlugin
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for ScriptPlugin {
|
||||
fn transform_file(&self, plugins: &PluginChain, vfs_item: &VfsItem) -> TransformFileResult {
|
||||
match vfs_item {
|
||||
&VfsItem::File { ref contents, ref name } => {
|
||||
let (class_name, rbx_name) = {
|
||||
if let Some(captures) = SERVER_PATTERN.captures(name) {
|
||||
("Script".to_string(), captures.get(1).unwrap().as_str().to_string())
|
||||
} else if let Some(captures) = CLIENT_PATTERN.captures(name) {
|
||||
("LocalScript".to_string(), captures.get(1).unwrap().as_str().to_string())
|
||||
} else if let Some(captures) = MODULE_PATTERN.captures(name) {
|
||||
("ModuleScript".to_string(), captures.get(1).unwrap().as_str().to_string())
|
||||
} else {
|
||||
return TransformFileResult::Pass;
|
||||
}
|
||||
};
|
||||
|
||||
let mut properties = HashMap::new();
|
||||
|
||||
properties.insert("Source".to_string(), RbxValue::String {
|
||||
value: contents.clone(),
|
||||
});
|
||||
|
||||
TransformFileResult::Value(Some(RbxItem {
|
||||
name: rbx_name,
|
||||
class_name: class_name,
|
||||
children: Vec::new(),
|
||||
properties,
|
||||
}))
|
||||
},
|
||||
&VfsItem::Dir { ref children, ref name } => {
|
||||
let init_item = {
|
||||
let maybe_item = children.get(SERVER_INIT)
|
||||
.or(children.get(CLIENT_INIT))
|
||||
.or(children.get(MODULE_INIT));
|
||||
|
||||
match maybe_item {
|
||||
Some(v) => v,
|
||||
None => return TransformFileResult::Pass,
|
||||
}
|
||||
};
|
||||
|
||||
let mut rbx_item = match self.transform_file(plugins, init_item) {
|
||||
TransformFileResult::Value(Some(item)) => item,
|
||||
_ => {
|
||||
eprintln!("Inconsistency detected in ScriptPlugin!");
|
||||
return TransformFileResult::Pass;
|
||||
},
|
||||
};
|
||||
|
||||
rbx_item.name.clear();
|
||||
rbx_item.name.push_str(name);
|
||||
|
||||
for (child_name, child_item) in children {
|
||||
if child_name == init_item.name() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match plugins.transform_file(child_item) {
|
||||
Some(child_rbx_item) => {
|
||||
rbx_item.children.push(child_rbx_item);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
TransformFileResult::Value(Some(rbx_item))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_file_change(&self, route: &Route) -> FileChangeResult {
|
||||
let leaf = match route.last() {
|
||||
Some(v) => v,
|
||||
None => return FileChangeResult::Pass,
|
||||
};
|
||||
|
||||
let is_init = leaf == SERVER_INIT
|
||||
|| leaf == CLIENT_INIT
|
||||
|| leaf == MODULE_INIT;
|
||||
|
||||
if is_init {
|
||||
let mut changed = route.clone();
|
||||
changed.pop();
|
||||
|
||||
FileChangeResult::MarkChanged(Some(vec![changed]))
|
||||
} else {
|
||||
FileChangeResult::Pass
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_rbx_change(&self, route: &Route, rbx_item: &RbxItem) -> RbxChangeResult {
|
||||
RbxChangeResult::Pass
|
||||
}
|
||||
}
|
||||
18
src/rbx.rs
Normal file
18
src/rbx.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RbxItem {
|
||||
pub name: String,
|
||||
pub class_name: String,
|
||||
pub children: Vec<RbxItem>,
|
||||
pub properties: HashMap<String, RbxValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
pub enum RbxValue {
|
||||
String {
|
||||
value: String,
|
||||
},
|
||||
}
|
||||
40
src/vfs.rs
40
src/vfs.rs
@@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
use core::Config;
|
||||
use plugin::PluginChain;
|
||||
|
||||
/// Represents a virtual layer over multiple parts of the filesystem.
|
||||
///
|
||||
@@ -24,6 +25,8 @@ pub struct Vfs {
|
||||
/// created, along with a timestamp denoting when.
|
||||
pub change_history: Vec<VfsChange>,
|
||||
|
||||
plugin_chain: &'static PluginChain,
|
||||
|
||||
config: Config,
|
||||
}
|
||||
|
||||
@@ -37,16 +40,26 @@ pub struct VfsChange {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
pub enum VfsItem {
|
||||
File { contents: String },
|
||||
Dir { children: HashMap<String, VfsItem> },
|
||||
File { name: String, contents: String },
|
||||
Dir { name: String, children: HashMap<String, VfsItem> },
|
||||
}
|
||||
|
||||
impl VfsItem {
|
||||
pub fn name(&self) -> &String {
|
||||
match self {
|
||||
&VfsItem::File { ref name, .. } => name,
|
||||
&VfsItem::Dir { ref name, .. } => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vfs {
|
||||
pub fn new(config: Config) -> Vfs {
|
||||
pub fn new(config: Config, plugin_chain: &'static PluginChain) -> Vfs {
|
||||
Vfs {
|
||||
partitions: HashMap::new(),
|
||||
start_time: Instant::now(),
|
||||
change_history: Vec::new(),
|
||||
plugin_chain,
|
||||
config,
|
||||
}
|
||||
}
|
||||
@@ -78,6 +91,7 @@ impl Vfs {
|
||||
}
|
||||
|
||||
fn read_dir<P: AsRef<Path>>(&self, path: P) -> Result<VfsItem, ()> {
|
||||
let path = path.as_ref();
|
||||
let reader = match fs::read_dir(path) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Err(()),
|
||||
@@ -104,11 +118,13 @@ impl Vfs {
|
||||
}
|
||||
|
||||
Ok(VfsItem::Dir {
|
||||
name: path.file_name().unwrap().to_string_lossy().into_owned(),
|
||||
children,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_file<P: AsRef<Path>>(&self, path: P) -> Result<VfsItem, ()> {
|
||||
let path = path.as_ref();
|
||||
let mut file = match File::open(path) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Err(()),
|
||||
@@ -122,6 +138,7 @@ impl Vfs {
|
||||
}
|
||||
|
||||
Ok(VfsItem::File {
|
||||
name: path.file_name().unwrap().to_string_lossy().into_owned(),
|
||||
contents,
|
||||
})
|
||||
}
|
||||
@@ -151,13 +168,20 @@ impl Vfs {
|
||||
|
||||
pub fn add_change(&mut self, timestamp: f64, route: Vec<String>) {
|
||||
if self.config.verbose {
|
||||
println!("Added change {:?}", route);
|
||||
println!("Received change {:?}, running through plugins...", route);
|
||||
}
|
||||
|
||||
self.change_history.push(VfsChange {
|
||||
timestamp,
|
||||
route,
|
||||
});
|
||||
match self.plugin_chain.handle_file_change(&route) {
|
||||
Some(routes) => {
|
||||
for route in routes {
|
||||
self.change_history.push(VfsChange {
|
||||
timestamp,
|
||||
route,
|
||||
});
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn changes_since(&self, timestamp: f64) -> &[VfsChange] {
|
||||
|
||||
147
src/web.rs
147
src/web.rs
@@ -1,6 +1,5 @@
|
||||
use std::io::Read;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
use rouille;
|
||||
use serde;
|
||||
@@ -8,9 +7,11 @@ use serde_json;
|
||||
|
||||
use core::Config;
|
||||
use project::Project;
|
||||
use vfs::{Vfs, VfsChange, VfsItem};
|
||||
use vfs::{Vfs, VfsChange};
|
||||
use rbx::RbxItem;
|
||||
use plugin::PluginChain;
|
||||
|
||||
static MAX_BODY_SIZE: usize = 25 * 1024 * 1025; // 25 MiB
|
||||
static MAX_BODY_SIZE: usize = 25 * 1024 * 1024; // 25 MiB
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -25,7 +26,7 @@ struct ServerInfo<'a> {
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ReadResult<'a> {
|
||||
items: Vec<Option<VfsItem>>,
|
||||
items: Vec<Option<RbxItem>>,
|
||||
server_id: &'a str,
|
||||
current_time: f64,
|
||||
}
|
||||
@@ -38,6 +39,13 @@ struct ChangesResult<'a> {
|
||||
current_time: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WriteSpecifier {
|
||||
route: String,
|
||||
item: RbxItem,
|
||||
}
|
||||
|
||||
fn json<T: serde::Serialize>(value: T) -> rouille::Response {
|
||||
let data = serde_json::to_string(&value).unwrap();
|
||||
rouille::Response::from_data("application/json", data)
|
||||
@@ -93,78 +101,91 @@ where
|
||||
Some(parsed)
|
||||
}
|
||||
|
||||
pub fn start(config: Config, project: Project, vfs: Arc<Mutex<Vfs>>) {
|
||||
pub fn start(config: Config, project: Project, plugin_chain: &'static PluginChain, vfs: Arc<Mutex<Vfs>>) {
|
||||
let address = format!("localhost:{}", config.port);
|
||||
|
||||
let server_id = config.server_id.to_string();
|
||||
|
||||
thread::spawn(move || {
|
||||
rouille::start_server(address, move |request| {
|
||||
router!(request,
|
||||
(GET) (/) => {
|
||||
let current_time = {
|
||||
let vfs = vfs.lock().unwrap();
|
||||
|
||||
vfs.current_time()
|
||||
};
|
||||
|
||||
json(ServerInfo {
|
||||
server_version: env!("CARGO_PKG_VERSION"),
|
||||
protocol_version: 0,
|
||||
server_id: &server_id,
|
||||
project: &project,
|
||||
current_time,
|
||||
})
|
||||
},
|
||||
|
||||
(GET) (/changes/{ last_time: f64 }) => {
|
||||
rouille::start_server(address, move |request| {
|
||||
router!(request,
|
||||
(GET) (/) => {
|
||||
let current_time = {
|
||||
let vfs = vfs.lock().unwrap();
|
||||
|
||||
vfs.current_time()
|
||||
};
|
||||
|
||||
json(ServerInfo {
|
||||
server_version: env!("CARGO_PKG_VERSION"),
|
||||
protocol_version: 1,
|
||||
server_id: &server_id,
|
||||
project: &project,
|
||||
current_time,
|
||||
})
|
||||
},
|
||||
|
||||
(GET) (/changes/{ last_time: f64 }) => {
|
||||
let vfs = vfs.lock().unwrap();
|
||||
let current_time = vfs.current_time();
|
||||
let changes = vfs.changes_since(last_time);
|
||||
|
||||
json(ChangesResult {
|
||||
changes,
|
||||
server_id: &server_id,
|
||||
current_time,
|
||||
})
|
||||
},
|
||||
|
||||
(POST) (/read) => {
|
||||
let read_request: Vec<Vec<String>> = match read_json(&request) {
|
||||
Some(v) => v,
|
||||
None => return rouille::Response::empty_400(),
|
||||
};
|
||||
|
||||
let (items, current_time) = {
|
||||
let vfs = vfs.lock().unwrap();
|
||||
|
||||
let current_time = vfs.current_time();
|
||||
let changes = vfs.changes_since(last_time);
|
||||
|
||||
json(ChangesResult {
|
||||
changes,
|
||||
server_id: &server_id,
|
||||
current_time,
|
||||
})
|
||||
},
|
||||
let mut items = Vec::new();
|
||||
|
||||
(POST) (/read) => {
|
||||
let read_request: Vec<Vec<String>> = match read_json(&request) {
|
||||
Some(v) => v,
|
||||
None => return rouille::Response::empty_400(),
|
||||
};
|
||||
|
||||
let (items, current_time) = {
|
||||
let vfs = vfs.lock().unwrap();
|
||||
|
||||
let current_time = vfs.current_time();
|
||||
|
||||
let mut items = Vec::new();
|
||||
|
||||
for route in &read_request {
|
||||
match vfs.read(&route) {
|
||||
Ok(v) => items.push(Some(v)),
|
||||
Err(_) => items.push(None),
|
||||
}
|
||||
for route in &read_request {
|
||||
match vfs.read(&route) {
|
||||
Ok(v) => items.push(Some(v)),
|
||||
Err(_) => items.push(None),
|
||||
}
|
||||
}
|
||||
|
||||
(items, current_time)
|
||||
};
|
||||
(items, current_time)
|
||||
};
|
||||
|
||||
json(ReadResult {
|
||||
server_id: &server_id,
|
||||
items,
|
||||
current_time,
|
||||
let rbx_items = items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
match *item {
|
||||
Some(ref item) => plugin_chain.transform_file(item),
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
},
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(POST) (/write) => {
|
||||
rouille::Response::empty_404()
|
||||
},
|
||||
json(ReadResult {
|
||||
server_id: &server_id,
|
||||
items: rbx_items,
|
||||
current_time,
|
||||
})
|
||||
},
|
||||
|
||||
_ => rouille::Response::empty_404()
|
||||
)
|
||||
});
|
||||
(POST) (/write) => {
|
||||
let _write_request: Vec<WriteSpecifier> = match read_json(&request) {
|
||||
Some(v) => v,
|
||||
None => return rouille::Response::empty_400(),
|
||||
};
|
||||
|
||||
rouille::Response::empty_404()
|
||||
},
|
||||
|
||||
_ => rouille::Response::empty_404()
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user