From 72bc77f1d5a5130230498863590d599df0e241ca Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Sun, 26 Aug 2018 01:03:53 -0700 Subject: [PATCH] WIP: Epiphany Refactor (#85) --- README.md | 6 +- integration/.gitignore | 2 + integration/Cargo.lock | 4 + integration/Cargo.toml | 6 + integration/README.md | 2 + integration/src/main.rs | 32 ++ plugin/README.md | 5 + plugin/loadEnvironment.lua | 37 ++ plugin/modules/lemur | 2 +- plugin/rojo.json | 20 +- plugin/runTest.lua | 15 + plugin/spec.lua | 31 +- plugin/src/ApiContext.lua | 22 +- plugin/src/Http.lua | 2 +- plugin/src/Session.lua | 54 +- plugin/testBootstrap.server.lua | 2 + plugin/tests/empty.lua | 5 + plugin/tests/runTests.server.lua | 2 - server/Cargo.lock | 473 +++++++++-------- server/README.md | 4 + server/src/bin.rs | 17 +- server/src/commands/init.rs | 2 +- server/src/commands/mod.rs | 2 +- server/src/commands/serve.rs | 53 +- server/src/file_route.rs | 166 ------ server/src/id.rs | 2 +- server/src/lib.rs | 14 +- .../{message_session.rs => message_queue.rs} | 38 +- server/src/partition.rs | 13 - server/src/partition_watcher.rs | 67 --- server/src/pathext.rs | 2 +- server/src/project.rs | 437 ++++++++-------- server/src/rbx.rs | 131 +++-- server/src/rbx_session.rs | 306 ----------- server/src/session.rs | 178 ++++--- server/src/vfs.rs | 139 +++++ server/src/vfs_session.rs | 242 --------- server/src/web.rs | 176 +++---- server/src/web_util.rs | 2 +- server/tests/read_projects.rs | 36 ++ server/tests/web.rs | 483 ------------------ test-projects/empty/roblox-project.json | 4 + test-projects/empty/rojo.json | 5 - test-projects/foo.json | 26 + test-projects/one-partition/lib/a.lua | 1 - test-projects/one-partition/lib/a.server.lua | 1 - test-projects/one-partition/lib/b.client.lua | 1 - test-projects/one-partition/rojo.json | 10 - test-projects/partition-to-file/foo.lua | 1 - test-projects/partition-to-file/rojo.json | 10 - test-projects/single-sync-point/lib/main.lua | 0 .../single-sync-point/roblox-project.json | 11 + 52 files changed, 1145 insertions(+), 2157 deletions(-) create mode 100644 integration/.gitignore create mode 100644 integration/Cargo.lock create mode 100644 integration/Cargo.toml create mode 100644 integration/README.md create mode 100644 integration/src/main.rs create mode 100644 plugin/README.md create mode 100644 plugin/loadEnvironment.lua create mode 100644 plugin/runTest.lua create mode 100644 plugin/testBootstrap.server.lua create mode 100644 plugin/tests/empty.lua delete mode 100644 plugin/tests/runTests.server.lua create mode 100644 server/README.md delete mode 100644 server/src/file_route.rs rename server/src/{message_session.rs => message_queue.rs} (55%) delete mode 100644 server/src/partition.rs delete mode 100644 server/src/partition_watcher.rs delete mode 100644 server/src/rbx_session.rs create mode 100644 server/src/vfs.rs delete mode 100644 server/src/vfs_session.rs create mode 100644 server/tests/read_projects.rs delete mode 100644 server/tests/web.rs create mode 100644 test-projects/empty/roblox-project.json delete mode 100644 test-projects/empty/rojo.json create mode 100644 test-projects/foo.json delete mode 100644 test-projects/one-partition/lib/a.lua delete mode 100644 test-projects/one-partition/lib/a.server.lua delete mode 100644 test-projects/one-partition/lib/b.client.lua delete mode 100644 test-projects/one-partition/rojo.json delete mode 100644 test-projects/partition-to-file/foo.lua delete mode 100644 test-projects/partition-to-file/rojo.json create mode 100644 test-projects/single-sync-point/lib/main.lua create mode 100644 test-projects/single-sync-point/roblox-project.json diff --git a/README.md b/README.md index 50fc6c0a..d7a50670 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ Travis-CI Build Status - Current server version + + Latest server version + Rojo Documentation @@ -41,7 +43,7 @@ There are lots of other tools that sync scripts into Roblox or provide other too Here are a few, if you're looking for alternatives or supplements to Rojo: * [rbxmk by Anaminus](https://github.com/anaminus/rbxmk) -* [RbxRefresh by Osyris](https://github.com/osyrisrblx/RbxRefresh) +* [Rofresh](https://github.com/osyrisrblx/rofresh) and [RbxRefresh](https://github.com/osyrisrblx/RbxRefresh) by [Osyris](https://github.com/osyrisrblx) * [Studio Bridge by Vocksel](https://github.com/vocksel/studio-bridge) * [RbxSync by evaera](https://github.com/evaera/RbxSync) * [CodeSync](https://github.com/MemoryPenguin/CodeSync) and [rbx-exteditor](https://github.com/MemoryPenguin/rbx-exteditor) by [MemoryPenguin](https://github.com/MemoryPenguin) diff --git a/integration/.gitignore b/integration/.gitignore new file mode 100644 index 00000000..a79a8002 --- /dev/null +++ b/integration/.gitignore @@ -0,0 +1,2 @@ +/target/ +**/*.rs.bk \ No newline at end of file diff --git a/integration/Cargo.lock b/integration/Cargo.lock new file mode 100644 index 00000000..02b1e530 --- /dev/null +++ b/integration/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "integration" +version = "0.1.0" + diff --git a/integration/Cargo.toml b/integration/Cargo.toml new file mode 100644 index 00000000..769e48aa --- /dev/null +++ b/integration/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "integration" +version = "0.1.0" +authors = ["Lucien Greathouse "] + +[dependencies] diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 00000000..e211270b --- /dev/null +++ b/integration/README.md @@ -0,0 +1,2 @@ +# Rojo Integration Runner +This is a WIP test runner designed for Rojo. It will eventually start up the Rojo server and plugin and test functionality end-to-end. \ No newline at end of file diff --git a/integration/src/main.rs b/integration/src/main.rs new file mode 100644 index 00000000..4e4883b1 --- /dev/null +++ b/integration/src/main.rs @@ -0,0 +1,32 @@ +use std::{ + path::Path, + process::Command, + thread, + time::Duration, +}; + +fn main() { + let plugin_path = Path::new("../plugin"); + let server_path = Path::new("../server"); + let tests_path = Path::new("../tests"); + + let server = Command::new("cargo") + .args(&["run", "--", "serve", "../test-projects/empty"]) + .current_dir(server_path) + .spawn(); + + thread::sleep(Duration::from_millis(1000)); + + // TODO: Wait for server to start responding on the right port + + let test_client = Command::new("lua") + .args(&["runTest.lua", "tests/empty.lua"]) + .current_dir(plugin_path) + .spawn(); + + thread::sleep(Duration::from_millis(300)); + + // TODO: Collect output from the client for success/failure? + + println!("Dying!"); +} diff --git a/plugin/README.md b/plugin/README.md new file mode 100644 index 00000000..f3f68394 --- /dev/null +++ b/plugin/README.md @@ -0,0 +1,5 @@ +# Rojo Plugin + +This is the source to the Rojo Roblox Studio plugin. + +Documentation is WIP. \ No newline at end of file diff --git a/plugin/loadEnvironment.lua b/plugin/loadEnvironment.lua new file mode 100644 index 00000000..e7065daa --- /dev/null +++ b/plugin/loadEnvironment.lua @@ -0,0 +1,37 @@ +--[[ + Loads the Rojo plugin and all of its dependencies. +]] + +local function loadEnvironment() + -- If you add any dependencies, add them to this table so they'll be loaded! + local LOAD_MODULES = { + {"src", "Rojo"}, + {"modules/promise/lib", "Promise"}, + {"modules/testez/lib", "TestEZ"}, + } + + -- This makes sure we can load Lemur and other libraries that depend on init.lua + package.path = package.path .. ";?/init.lua" + + -- If this fails, make sure you've run `lua bin/install-dependencies.lua` first! + local lemur = require("modules.lemur") + + -- Create a virtual Roblox tree + local habitat = lemur.Habitat.new() + + -- We'll put all of our library code and dependencies here + local modules = lemur.Instance.new("Folder") + modules.Name = "Modules" + modules.Parent = habitat.game:GetService("ReplicatedStorage") + + -- Load all of the modules specified above + for _, module in ipairs(LOAD_MODULES) do + local container = habitat:loadFromFs(module[1]) + container.Name = module[2] + container.Parent = modules + end + + return habitat, modules +end + +return loadEnvironment \ No newline at end of file diff --git a/plugin/modules/lemur b/plugin/modules/lemur index 86b33cdf..96d4166a 160000 --- a/plugin/modules/lemur +++ b/plugin/modules/lemur @@ -1 +1 @@ -Subproject commit 86b33cdfb46fd1e736f8ee1d8861d1b58ffa987f +Subproject commit 96d4166a2d9e146e6955eeacccc0c79a86a177f5 diff --git a/plugin/rojo.json b/plugin/rojo.json index 17f5548f..2276882c 100644 --- a/plugin/rojo.json +++ b/plugin/rojo.json @@ -4,31 +4,31 @@ "partitions": { "plugin": { "path": "src", - "target": "ReplicatedStorage.Rojo.modules.Plugin" + "target": "ReplicatedStorage.Rojo.Modules.Plugin" }, "modules/roact": { "path": "modules/roact/lib", - "target": "ReplicatedStorage.Rojo.modules.Roact" + "target": "ReplicatedStorage.Rojo.Modules.Roact" }, "modules/rodux": { "path": "modules/rodux/lib", - "target": "ReplicatedStorage.Rojo.modules.Rodux" + "target": "ReplicatedStorage.Rojo.Modules.Rodux" }, "modules/roact-rodux": { "path": "modules/roact-rodux/lib", - "target": "ReplicatedStorage.Rojo.modules.RoactRodux" + "target": "ReplicatedStorage.Rojo.Modules.RoactRodux" + }, + "modules/promise": { + "path": "modules/promise/lib", + "target": "ReplicatedStorage.Rojo.Modules.Promise" }, "modules/testez": { "path": "modules/testez/lib", "target": "ReplicatedStorage.TestEZ" }, - "modules/promise": { - "path": "modules/promise/lib", - "target": "ReplicatedStorage.Rojo.modules.Promise" - }, "tests": { - "path": "tests", - "target": "TestService" + "path": "testBootstrap.server.lua", + "target": "TestService.testBootstrap" } } } \ No newline at end of file diff --git a/plugin/runTest.lua b/plugin/runTest.lua new file mode 100644 index 00000000..a3a682d0 --- /dev/null +++ b/plugin/runTest.lua @@ -0,0 +1,15 @@ +local loadEnvironment = require("loadEnvironment") + +local testPath = assert((...), "Please specify a path to a test file.") + +local habitat = loadEnvironment() + +local testModule = habitat:loadFromFs(testPath) + +if testModule == nil then + error("Couldn't find test file at " .. testPath) +end + +print("Starting test module.") + +habitat:require(testModule) \ No newline at end of file diff --git a/plugin/spec.lua b/plugin/spec.lua index 102093f1..25e23181 100644 --- a/plugin/spec.lua +++ b/plugin/spec.lua @@ -2,37 +2,14 @@ Loads our library and all of its dependencies, then runs tests using TestEZ. ]] --- If you add any dependencies, add them to this table so they'll be loaded! -local LOAD_MODULES = { - {"src", "plugin"}, - {"modules/promise/lib", "Promise"}, - {"modules/testez/lib", "TestEZ"}, -} +local loadEnvironment = require("loadEnvironment") --- This makes sure we can load Lemur and other libraries that depend on init.lua -package.path = package.path .. ";?/init.lua" - --- If this fails, make sure you've run `lua bin/install-dependencies.lua` first! -local lemur = require("modules.lemur") - --- Create a virtual Roblox tree -local habitat = lemur.Habitat.new() - --- We'll put all of our library code and dependencies here -local Root = lemur.Instance.new("Folder") -Root.Name = "Root" - --- Load all of the modules specified above -for _, module in ipairs(LOAD_MODULES) do - local container = habitat:loadFromFs(module[1]) - container.Name = module[2] - container.Parent = Root -end +local habitat, modules = loadEnvironment() -- Load TestEZ and run our tests -local TestEZ = habitat:require(Root.TestEZ) +local TestEZ = habitat:require(modules.TestEZ) -local results = TestEZ.TestBootstrap:run(Root.plugin, TestEZ.Reporters.TextReporter) +local results = TestEZ.TestBootstrap:run(modules.Rojo, TestEZ.Reporters.TextReporter) -- Did something go wrong? if results.failureCount > 0 then diff --git a/plugin/src/ApiContext.lua b/plugin/src/ApiContext.lua index d8d301e1..19dd19eb 100644 --- a/plugin/src/ApiContext.lua +++ b/plugin/src/ApiContext.lua @@ -19,14 +19,15 @@ setmetatable(ApiContext.Error, { end }) -function ApiContext.new(url, onMessage) - assert(type(url) == "string") +function ApiContext.new(baseUrl, onMessage) + assert(type(baseUrl) == "string") assert(type(onMessage) == "function") local context = { - url = url, + baseUrl = baseUrl, onMessage = onMessage, serverId = nil, + rootInstanceId = nil, connected = false, messageCursor = -1, partitionRoutes = nil, @@ -38,7 +39,9 @@ function ApiContext.new(url, onMessage) end function ApiContext:connect() - return Http.get(self.url .. "/api/rojo") + local url = ("%s/api/rojo"):format(self.baseUrl) + + return Http.get(url) :andThen(function(response) local body = response:json() @@ -61,15 +64,18 @@ function ApiContext:connect() self.serverId = body.serverId self.connected = true self.partitionRoutes = body.partitions + self.rootInstanceId = body.rootInstanceId end) end -function ApiContext:readAll() +function ApiContext:read(ids) if not self.connected then return Promise.reject() end - return Http.get(self.url .. "/api/read_all") + local url = ("%s/api/read/%s"):format(self.baseUrl, table.concat(ids, ",")) + + return Http.get(url) :andThen(function(response) local body = response:json() @@ -92,7 +98,9 @@ function ApiContext:retrieveMessages() return Promise.reject() end - return Http.get(self.url .. "/api/subscribe/" .. self.messageCursor) + local url = ("%s/api/subscribe/%s"):format(self.baseUrl, self.messageCursor) + + return Http.get(url) :andThen(function(response) local body = response:json() diff --git a/plugin/src/Http.lua b/plugin/src/Http.lua index d6dd9cdb..57cedc15 100644 --- a/plugin/src/Http.lua +++ b/plugin/src/Http.lua @@ -1,6 +1,6 @@ local HttpService = game:GetService("HttpService") -local HTTP_DEBUG = false +local HTTP_DEBUG = true local Promise = require(script.Parent.Parent.Promise) diff --git a/plugin/src/Session.lua b/plugin/src/Session.lua index 47d8d5d0..4eb1d012 100644 --- a/plugin/src/Session.lua +++ b/plugin/src/Session.lua @@ -11,57 +11,7 @@ function Session.new() setmetatable(self, Session) - local function createFoldersUntil(location, route) - for i = 1, #route - 1 do - local piece = route[i] - - local child = location:FindFirstChild(piece) - - if child == nil then - child = Instance.new("Folder") - child.Name = piece - child.Parent = location - end - - location = child - end - - return location - end - - local function reify(instancesById, id) - local object = instancesById[tostring(id)] - local instance = Instance.new(object.className) - instance.Name = object.name - - for key, property in pairs(object.properties) do - instance[key] = property.value - end - - for _, childId in ipairs(object.children) do - reify(instancesById, childId).Parent = instance - end - - return instance - end - local api - local function readAll() - print("Reading all...") - - return api:readAll() - :andThen(function(response) - for partitionName, partitionRoute in pairs(api.partitionRoutes) do - local parent = createFoldersUntil(game, partitionRoute) - - local rootInstanceId = response.partitionInstances[partitionName] - - print("Root for", partitionName, "is", rootInstanceId) - - reify(response.instances, rootInstanceId).Parent = parent - end - end) - end api = ApiContext.new(REMOTE_URL, function(message) if message.type == "InstanceChanged" then @@ -73,7 +23,9 @@ function Session.new() end) api:connect() - :andThen(readAll) + :andThen(function() + return api:read({api.rootInstanceId}) + end) :andThen(function() return api:retrieveMessages() end) diff --git a/plugin/testBootstrap.server.lua b/plugin/testBootstrap.server.lua new file mode 100644 index 00000000..ae53be9f --- /dev/null +++ b/plugin/testBootstrap.server.lua @@ -0,0 +1,2 @@ +local TestEZ = require(game.ReplicatedStorage.TestEZ) +TestEZ.TestBootstrap:run(game.ReplicatedStorage.Rojo.plugin) \ No newline at end of file diff --git a/plugin/tests/empty.lua b/plugin/tests/empty.lua new file mode 100644 index 00000000..bfda7fad --- /dev/null +++ b/plugin/tests/empty.lua @@ -0,0 +1,5 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Session = require(ReplicatedStorage.Modules.Rojo.Session) + +Session.new() \ No newline at end of file diff --git a/plugin/tests/runTests.server.lua b/plugin/tests/runTests.server.lua deleted file mode 100644 index be1a94d5..00000000 --- a/plugin/tests/runTests.server.lua +++ /dev/null @@ -1,2 +0,0 @@ -local TestEZ = require(game.ReplicatedStorage.TestEZ) -TestEZ.TestBootstrap:run(game.ReplicatedStorage.Rojo.plugin) diff --git a/server/Cargo.lock b/server/Cargo.lock index bf03e25e..3e0dfd05 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1,14 +1,14 @@ [[package]] name = "adler32" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aho-corasick" -version = "0.6.4" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -16,7 +16,7 @@ name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -26,12 +26,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "atty" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -39,7 +39,7 @@ name = "base64" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -55,7 +55,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -64,7 +64,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -73,7 +73,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "brotli-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -92,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.2.2" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -102,7 +102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -111,17 +111,17 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "chrono" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -131,16 +131,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.31.2" +version = "2.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -156,16 +164,11 @@ name = "deflate" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "gzip-header 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dtoa" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "encoding" version = "0.2.33" @@ -236,9 +239,9 @@ name = "filetime" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -248,7 +251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -256,7 +259,7 @@ name = "fsevent-sys" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -264,7 +267,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -289,17 +292,17 @@ dependencies = [ [[package]] name = "httparse" -version = "1.2.4" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "idna" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -307,12 +310,12 @@ name = "inotify" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -326,12 +329,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "libc" -version = "0.2.40" +version = "0.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -339,20 +345,20 @@ name = "log" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "log" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "matches" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -360,15 +366,15 @@ name = "memchr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -381,7 +387,7 @@ dependencies = [ [[package]] name = "mime_guess" -version = "1.8.4" +version = "1.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -396,13 +402,13 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -412,7 +418,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -423,10 +429,10 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -435,12 +441,12 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -449,12 +455,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "notify" -version = "4.0.3" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -463,9 +469,9 @@ dependencies = [ "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -474,26 +480,26 @@ name = "num" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-integer" -version = "0.1.36" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-iter" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -501,12 +507,12 @@ name = "num-traits" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -514,7 +520,7 @@ name = "num_cpus" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -545,7 +551,7 @@ version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -553,13 +559,13 @@ name = "phf_shared" version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" -version = "0.3.8" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -567,10 +573,10 @@ dependencies = [ [[package]] name = "quote" -version = "0.5.2" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -579,23 +585,40 @@ version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "redox_syscall" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -603,24 +626,24 @@ name = "redox_termios" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.0.0" +version = "1.0.4" 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.6.0 (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)", + "aho-corasick 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -631,24 +654,24 @@ name = "remove_dir_all" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rojo" version = "0.5.0" dependencies = [ - "clap 2.31.2 (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.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "rouille 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -658,21 +681,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "brotli2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -680,6 +703,11 @@ name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ryu" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "safemem" version = "0.2.0" @@ -687,35 +715,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "same-file" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" -version = "1.0.51" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.51" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.17" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -725,7 +753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "siphasher" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -740,11 +768,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.13.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -753,20 +781,20 @@ name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempfile" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -783,26 +811,25 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "textwrap" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thread_local" -version = "0.3.5" +version = "0.3.6" 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)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -815,12 +842,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -841,7 +868,7 @@ name = "twoway" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -854,7 +881,7 @@ name = "unicase" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -862,17 +889,17 @@ name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-normalization" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-width" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -880,37 +907,29 @@ name = "unicode-xid" version = "0.1.0" 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" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "url" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "utf8-ranges" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -924,26 +943,22 @@ dependencies = [ [[package]] name = "vec_map" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "version_check" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "walkdir" -version = "2.1.4" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -953,7 +968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -970,6 +985,14 @@ name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -985,29 +1008,29 @@ dependencies = [ ] [metadata] -"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" -"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum aho-corasick 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee94e9463ccb9d681e7b708082687b2c56d2bd420ca8a3d3157d27d59508ec0" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" -"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum base64 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5032d51da2741729bfdaeb2664d9b8c6d9fd1e2b90715c660b6def36628499c2" "checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum brotli-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb50f54b2e0c671b7ef1637a76237ebacbb293be179440d5d65ca288e42116bb" "checksum brotli2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea9d0bbab1235017a09226b079ed733bca4bf9ecb6b6102bd01aac79ea082dca" "checksum buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b9279646319ff816b05fb5897883ece50d7d854d12b59992683d4f8a71b0f949" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" -"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27" -"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" -"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e48d85528df61dc964aa43c5f6ca681a19cfa74939b2348d204bd08a981f2fb0" "checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" -"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31" -"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" "checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" @@ -1023,86 +1046,88 @@ dependencies = [ "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum gzip-header 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9fcfe1c9ee125342355b2467bc29b9dfcb2124fcae27edb9cee6f4cc5ecd40" -"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" -"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" -"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" "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.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" -"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" +"checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" -"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -"checksum mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b7e2b09d08313f84e0fb82d13a4d859109a17543fe9af3b6d941dc1431f7de79" +"checksum mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4c0961143b8efdcfa29c3ae63281601b446a4a668165454b6c90f8024954c5" "checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" "checksum miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3e690c5df6b2f60acd45d56378981e827ff8295562fc8d34f573deb267a59cd1" "checksum multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92f54eb45230c3aa20864ccf0c277eeaeadcf5e437e91731db498dbf7fbe0ec6" -"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" -"checksum notify 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5c3812da3098f210a0bb440f9c008471a031aa4c1de07a264fdd75456c95a4eb" +"checksum notify 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d023ef40ca7680784b07be3f49913e1ea176da1b63949f2eb2fed96438bd7f42" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" -"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" +"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" -"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" -"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" +"checksum proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ee5697238f0d893c7f0ecc59c0999f18d2af85e424de441178bcacc9f9e6cf67" +"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" -"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3" -"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b" +"checksum regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "67d0301b0c6804eca7e3c275119d0b01ff3b7ab9258a65709e608a66312a1025" +"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rouille 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc1f8407af80b0630983b2c1f1860dda1960fdec8d3ee75ba8db14937756d3a0" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" -"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" -"checksum serde 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "21924cc18e5281f232a17c040355fac97732b42cf019c24996a1642bcb169cdb" -"checksum serde_derive 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9c624a90bec6fe9bc60d275d7af71c72c26b24cd6c6776d8e344dc4044caa3e2" -"checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1" +"checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" +"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87" +"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119" +"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" -"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "61b8f1b737f929c6516ba46a3133fd6d5215ad8a62f66760f851f7048aebedfb" +"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b" +"checksum tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4b103c6d08d323b92ff42c8ce62abcd83ca8efa7fd5bf7927efefec75f58c76" "checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" -"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" "checksum tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2f4d55c9a213880d1f0c89ded183f209c6e45b912ca6c7df6f93c163773572e1" "checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90d662d111b0dbb08a180f2761026cba648c258023c355954a7c00e00e354636" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"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.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" +"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" "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 version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" +"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/server/README.md b/server/README.md new file mode 100644 index 00000000..b3801934 --- /dev/null +++ b/server/README.md @@ -0,0 +1,4 @@ +# Rojo Server +This is the source to the Rojo server. + +Documentation is WIP. \ No newline at end of file diff --git a/server/src/bin.rs b/server/src/bin.rs index bf20429e..86232c92 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -42,20 +42,7 @@ fn main() { None => std::env::current_dir().unwrap(), }; - let port = { - match sub_matches.value_of("port") { - Some(source) => match source.parse::() { - Ok(value) => Some(value), - Err(_) => { - eprintln!("Invalid port '{}'", source); - process::exit(1); - }, - }, - None => None, - } - }; - - librojo::commands::serve(&project_path, port); + librojo::commands::serve(&project_path); }, _ => { eprintln!("Please specify a subcommand!"); @@ -63,4 +50,4 @@ fn main() { process::exit(1); }, } -} +} \ No newline at end of file diff --git a/server/src/commands/init.rs b/server/src/commands/init.rs index 96cbbcb3..9050e072 100644 --- a/server/src/commands/init.rs +++ b/server/src/commands/init.rs @@ -13,4 +13,4 @@ pub fn init(project_path: &PathBuf) { process::exit(1); }, } -} +} \ No newline at end of file diff --git a/server/src/commands/mod.rs b/server/src/commands/mod.rs index 58023fa3..db228aa1 100644 --- a/server/src/commands/mod.rs +++ b/server/src/commands/mod.rs @@ -2,4 +2,4 @@ mod serve; mod init; pub use self::serve::*; -pub use self::init::*; +pub use self::init::*; \ No newline at end of file diff --git a/server/src/commands/serve.rs b/server/src/commands/serve.rs index 243ee335..7233c872 100644 --- a/server/src/commands/serve.rs +++ b/server/src/commands/serve.rs @@ -1,40 +1,39 @@ -use std::path::PathBuf; -use std::process; -use std::fs; +use std::{ + path::Path, + process, + sync::Arc, +}; -use rand; +use ::{ + project::Project, + web::Server, + session::Session, + roblox_studio, +}; -use project::Project; -use web::{self, WebConfig}; -use session::Session; -use roblox_studio; - -pub fn serve(project_dir: &PathBuf, override_port: Option) { - let server_id = rand::random::(); - - let project = match Project::load(project_dir) { - Ok(v) => { - println!("Using project from {}", fs::canonicalize(project_dir).unwrap().display()); - v - }, - Err(err) => { - eprintln!("{}", err); +pub fn serve(fuzzy_project_location: &Path) { + let project = match Project::load_fuzzy(fuzzy_project_location) { + Ok(project) => project, + Err(error) => { + eprintln!("Fatal: {}", error); process::exit(1); }, }; - let port = override_port.unwrap_or(project.serve_port); - + println!("Found project at {}", project.file_location.display()); println!("Using project {:#?}", project); roblox_studio::install_bundled_plugin().unwrap(); - let mut session = Session::new(project.clone()); - session.start(); + let session = Arc::new({ + let mut session = Session::new(project); + session.start().unwrap(); + session + }); - let web_config = WebConfig::from_session(server_id, port, &session); + let server = Server::new(Arc::clone(&session)); - println!("Server listening on port {}", port); + println!("Server listening on port 34872"); - web::start(web_config); -} + server.listen(34872); +} \ No newline at end of file diff --git a/server/src/file_route.rs b/server/src/file_route.rs deleted file mode 100644 index a691034e..00000000 --- a/server/src/file_route.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::path::{Path, PathBuf, Component}; - -use partition::Partition; - -// TODO: Change backing data structure to use a single allocation with slices -// taken out of it for each portion -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FileRoute { - pub partition: String, - pub route: Vec, -} - -impl FileRoute { - pub fn from_path(path: &Path, partition: &Partition) -> Option { - assert!(path.is_absolute()); - assert!(path.starts_with(&partition.path)); - - let relative_path = path.strip_prefix(&partition.path).ok()?; - let mut route = Vec::new(); - - for component in relative_path.components() { - match component { - Component::Normal(piece) => { - route.push(piece.to_string_lossy().into_owned()); - }, - _ => panic!("Unexpected path component: {:?}", component), - } - } - - Some(FileRoute { - partition: partition.name.clone(), - route, - }) - } - - pub fn parent(&self) -> Option { - if self.route.len() == 0 { - return None; - } - - let mut new_route = self.route.clone(); - new_route.pop(); - - Some(FileRoute { - partition: self.partition.clone(), - route: new_route, - }) - } - - /// Creates a PathBuf out of the `FileRoute` based on the given partition - /// `Path`. - pub fn to_path_buf(&self, partition_path: &Path) -> PathBuf { - let mut result = partition_path.to_path_buf(); - - for route_piece in &self.route { - result.push(route_piece); - } - - result - } - - /// Creates a version of the FileRoute with the given extra pieces appended - /// to the end. - pub fn extended_with(&self, pieces: &[&str]) -> FileRoute { - let mut result = self.clone(); - - for piece in pieces { - result.route.push(piece.to_string()); - } - - result - } - - pub fn file_name(&self, partition: &Partition) -> String { - if self.route.len() == 0 { - partition.path.file_name().unwrap().to_str().unwrap().to_string() - } else { - self.route.last().unwrap().clone() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[cfg(windows)] - const ROOT_PATH: &'static str = "C:\\"; - - #[cfg(not(windows))] - const ROOT_PATH: &'static str = "/"; - - #[test] - fn from_path_empty() { - let path = Path::new(ROOT_PATH).join("a/b/c"); - - let partition = Partition { - name: "foo".to_string(), - path: path.clone(), - target: vec![], - }; - - let route = FileRoute::from_path(&path, &partition); - - assert_eq!(route, Some(FileRoute { - partition: "foo".to_string(), - route: vec![], - })); - } - - #[test] - fn from_path_non_empty() { - let partition_path = Path::new(ROOT_PATH).join("a/b/c"); - - let inside_path = partition_path.join("d"); - - let partition = Partition { - name: "bar".to_string(), - path: partition_path, - target: vec![], - }; - - let route = FileRoute::from_path(&inside_path, &partition); - - assert_eq!(route, Some(FileRoute { - partition: "bar".to_string(), - route: vec!["d".to_string()], - })); - } - - #[test] - fn file_name_empty_route() { - let partition_path = Path::new(ROOT_PATH).join("a/b/c"); - - let partition = Partition { - name: "bar".to_string(), - path: partition_path, - target: vec![], - }; - - let route = FileRoute { - partition: "bar".to_string(), - route: vec![], - }; - - assert_eq!(route.file_name(&partition), "c"); - } - - #[test] - fn file_name_non_empty_route() { - let partition_path = Path::new(ROOT_PATH).join("a/b/c"); - - let partition = Partition { - name: "bar".to_string(), - path: partition_path, - target: vec![], - }; - - let route = FileRoute { - partition: "bar".to_string(), - route: vec!["foo".to_string(), "hello.lua".to_string()], - }; - - assert_eq!(route.file_name(&partition), "hello.lua"); - } -} \ No newline at end of file diff --git a/server/src/id.rs b/server/src/id.rs index a27a9d8c..98144cfa 100644 --- a/server/src/id.rs +++ b/server/src/id.rs @@ -18,4 +18,4 @@ fn it_gives_unique_numbers() { let b = get_id(); assert!(a != b); -} +} \ No newline at end of file diff --git a/server/src/lib.rs b/server/src/lib.rs index 2f806548..315d266c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -11,17 +11,13 @@ extern crate regex; extern crate tempfile; pub mod commands; -pub mod file_route; pub mod id; -pub mod message_session; -pub mod partition; -pub mod partition_watcher; +pub mod message_queue; pub mod pathext; pub mod project; pub mod rbx; -pub mod rbx_session; -pub mod session; -pub mod vfs_session; -pub mod web; -pub mod web_util; pub mod roblox_studio; +pub mod session; +pub mod vfs; +pub mod web; +pub mod web_util; \ No newline at end of file diff --git a/server/src/message_session.rs b/server/src/message_queue.rs similarity index 55% rename from server/src/message_session.rs rename to server/src/message_queue.rs index 85b2aa32..1029c7e1 100644 --- a/server/src/message_session.rs +++ b/server/src/message_queue.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::sync::{mpsc, Arc, RwLock, Mutex}; +use std::sync::{mpsc, RwLock, Mutex}; use id::{Id, get_id}; @@ -11,17 +11,16 @@ pub enum Message { }, } -#[derive(Clone)] -pub struct MessageSession { - pub messages: Arc>>, - pub message_listeners: Arc>>>, +pub struct MessageQueue { + messages: RwLock>, + message_listeners: Mutex>>, } -impl MessageSession { - pub fn new() -> MessageSession { - MessageSession { - messages: Arc::new(RwLock::new(Vec::new())), - message_listeners: Arc::new(Mutex::new(HashMap::new())), +impl MessageQueue { + pub fn new() -> MessageQueue { + MessageQueue { + messages: RwLock::new(Vec::new()), + message_listeners: Mutex::new(HashMap::new()), } } @@ -58,7 +57,20 @@ impl MessageSession { } } - pub fn get_message_cursor(&self) -> i32 { - self.messages.read().unwrap().len() as i32 - 1 + pub fn get_message_cursor(&self) -> u32 { + self.messages.read().unwrap().len() as u32 } -} + + pub fn get_messages_since(&self, cursor: u32) -> (u32, Vec) { + let messages = self.messages.read().unwrap(); + + let current_cursor = messages.len() as u32; + + // Cursor is out of bounds or there are no new messages + if cursor >= current_cursor { + return (current_cursor, Vec::new()); + } + + (current_cursor, messages[(cursor as usize)..].to_vec()) + } +} \ No newline at end of file diff --git a/server/src/partition.rs b/server/src/partition.rs deleted file mode 100644 index 9ef66664..00000000 --- a/server/src/partition.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::path::PathBuf; - -#[derive(Debug, Clone, PartialEq)] -pub struct Partition { - /// The unique name of this partition, used for debugging. - pub name: String, - - /// The path on the filesystem that this partition maps to. - pub path: PathBuf, - - /// The route to the Roblox instance that this partition maps to. - pub target: Vec, -} diff --git a/server/src/partition_watcher.rs b/server/src/partition_watcher.rs deleted file mode 100644 index 5885bb74..00000000 --- a/server/src/partition_watcher.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::mpsc::{channel, Sender}; -use std::time::Duration; -use std::thread; - -use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher, watcher}; - -use partition::Partition; -use vfs_session::FileChange; -use file_route::FileRoute; - -const WATCH_TIMEOUT_MS: u64 = 100; - -pub struct PartitionWatcher { - pub watcher: RecommendedWatcher, -} - -impl PartitionWatcher { - pub fn start_new(partition: Partition, tx: Sender) -> PartitionWatcher { - let (watch_tx, watch_rx) = channel(); - - let mut watcher = watcher(watch_tx, Duration::from_millis(WATCH_TIMEOUT_MS)).unwrap(); - - watcher.watch(&partition.path, RecursiveMode::Recursive).unwrap(); - - thread::spawn(move || { - loop { - match watch_rx.recv() { - Ok(event) => { - let file_change = match event { - DebouncedEvent::Create(path) => { - let route = FileRoute::from_path(&path, &partition).unwrap(); - FileChange::Created(route) - }, - DebouncedEvent::Write(path) => { - let route = FileRoute::from_path(&path, &partition).unwrap(); - FileChange::Updated(route) - }, - DebouncedEvent::Remove(path) => { - let route = FileRoute::from_path(&path, &partition).unwrap(); - FileChange::Deleted(route) - }, - DebouncedEvent::Rename(from_path, to_path) => { - let from_route = FileRoute::from_path(&from_path, &partition).unwrap(); - let to_route = FileRoute::from_path(&to_path, &partition).unwrap(); - FileChange::Moved(from_route, to_route) - }, - _ => continue, - }; - - match tx.send(file_change) { - Ok(_) => {}, - Err(_) => break, - } - }, - Err(_) => break, - }; - } - }); - - PartitionWatcher { - watcher, - } - } - - pub fn stop(self) { - } -} diff --git a/server/src/pathext.rs b/server/src/pathext.rs index 0b4c0f79..0b257cc8 100644 --- a/server/src/pathext.rs +++ b/server/src/pathext.rs @@ -24,4 +24,4 @@ where } else { root.join(value) } -} +} \ No newline at end of file diff --git a/server/src/project.rs b/server/src/project.rs index b0b95de7..b0a39e5a 100644 --- a/server/src/project.rs +++ b/server/src/project.rs @@ -1,246 +1,223 @@ -use std::collections::HashMap; -use std::fmt; -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; - -use rand::{self, Rng}; - +use std::{ + collections::HashMap, + fmt, + fs, + io, + path::{Path, PathBuf}, +}; use serde_json; -use partition::Partition; +pub static PROJECT_FILENAME: &'static str = "roblox-project.json"; -pub static PROJECT_FILENAME: &'static str = "rojo.json"; +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +enum SourceProjectNode { + Regular { + #[serde(rename = "$className")] + class_name: String, -#[derive(Debug)] -pub enum ProjectLoadError { - DidNotExist(PathBuf), - FailedToOpen(PathBuf), - FailedToRead(PathBuf), - InvalidJson(PathBuf, serde_json::Error), + // #[serde(rename = "$ignoreUnknown", default = "false")] + // ignore_unknown: bool, + + #[serde(flatten)] + children: HashMap, + }, + SyncPoint { + #[serde(rename = "$path")] + path: String, + } } -impl fmt::Display for ProjectLoadError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl SourceProjectNode { + pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode { match self { - &ProjectLoadError::InvalidJson(ref project_path, ref serde_err) => { - write!(f, "Found invalid JSON reading project: {}\nError: {}", project_path.display(), serde_err) + SourceProjectNode::Regular { class_name, mut children } => { + let mut new_children = HashMap::new(); + + for (node_name, node) in children.drain() { + new_children.insert(node_name, node.into_project_node(project_file_location)); + } + + ProjectNode::Regular { + class_name, + children: new_children, + } }, - &ProjectLoadError::FailedToOpen(ref project_path) | - &ProjectLoadError::FailedToRead(ref project_path) => { - write!(f, "Found project file, but failed to read it: {}", project_path.display()) - }, - &ProjectLoadError::DidNotExist(ref project_path) => { - write!(f, "Could not locate a project file at {}.\nUse 'rojo init' to create one.", project_path.display()) - }, - } - } -} + SourceProjectNode::SyncPoint { path: source_path } => { + let path = if Path::new(&source_path).is_absolute() { + PathBuf::from(source_path) + } else { + let project_folder_location = project_file_location.parent().unwrap(); + project_folder_location.join(source_path) + }; -#[derive(Debug)] -pub enum ProjectSaveError { - FailedToCreate, -} - -#[derive(Debug)] -pub enum ProjectInitError { - AlreadyExists, - FailedToCreate, - FailedToWrite, -} - -impl fmt::Display for ProjectInitError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &ProjectInitError::AlreadyExists => { - write!(f, "A project already exists at that location.") - }, - &ProjectInitError::FailedToCreate | - &ProjectInitError::FailedToWrite => { - write!(f, "Failed to write to the given location.") - }, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SourceProjectPartition { - /// A slash-separated path to a file or folder, relative to the project's - /// directory. - pub path: String, - - /// A dot-separated route to a Roblox instance, relative to game. - pub target: String, -} - -/// Represents a Rojo project in the format that's most convenient for users to -/// edit. This should generally line up with `Project`, but can diverge when -/// there's either compatibility shims or when the data structures that Rojo -/// want are too verbose to write in JSON but easy to convert from something -/// else. -// -/// Holds anything that can be configured with `rojo.json`. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct SourceProject { - pub name: String, - pub serve_port: u64, - pub partitions: HashMap, -} - -impl Default for SourceProject { - fn default() -> SourceProject { - SourceProject { - name: "new-project".to_string(), - serve_port: 8000, - partitions: HashMap::new(), - } - } -} - -/// Represents a Rojo project in the format that's convenient for Rojo to work -/// with. -#[derive(Debug, Clone)] -pub struct Project { - /// The path to the project file that this project is associated with. - pub project_path: PathBuf, - - /// The name of this project, used for user-facing labels. - pub name: String, - - /// The port that this project will run a web server on. - pub serve_port: u64, - - /// All of the project's partitions, laid out in an expanded way. - pub partitions: HashMap, -} - -impl Project { - fn from_source_project(source_project: SourceProject, project_path: PathBuf) -> Project { - let mut partitions = HashMap::new(); - - { - let project_directory = project_path.parent().unwrap(); - - for (partition_name, partition) in source_project.partitions.into_iter() { - let path = project_directory.join(&partition.path); - let target = partition.target - .split(".") - .map(String::from) - .collect::>(); - - partitions.insert(partition_name.clone(), Partition { + ProjectNode::SyncPoint { path, - target, - name: partition_name, - }); - } + } + }, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourceProject { + name: String, + tree: HashMap, +} + +impl SourceProject { + pub fn into_project(mut self, project_file_location: &Path) -> Project { + let mut tree = HashMap::new(); + + for (node_name, node) in self.tree.drain() { + tree.insert(node_name, node.into_project_node(project_file_location)); } Project { - project_path, - name: source_project.name, - serve_port: source_project.serve_port, - partitions, + name: self.name, + tree: tree, + file_location: PathBuf::from(project_file_location), } } - - fn as_source_project(&self) -> SourceProject { - let mut partitions = HashMap::new(); - - for partition in self.partitions.values() { - let path = partition.path.strip_prefix(&self.project_path) - .unwrap_or_else(|_| &partition.path) - .to_str() - .unwrap() - .to_string(); - - let target = partition.target.join("."); - - partitions.insert(partition.name.clone(), SourceProjectPartition { - path, - target, - }); - } - - SourceProject { - partitions, - name: self.name.clone(), - serve_port: self.serve_port, - } - } - - /// Initializes a new project inside the given folder path. - pub fn init>(location: T) -> Result { - let location = location.as_ref(); - let project_path = location.join(PROJECT_FILENAME); - - // We abort if the project file already exists. - fs::metadata(&project_path) - .map_err(|_| ProjectInitError::AlreadyExists)?; - - let mut file = File::create(&project_path) - .map_err(|_| ProjectInitError::FailedToCreate)?; - - // Try to give the project a meaningful name. - // If we can't, we'll just fall back to a default. - let name = match location.file_name() { - Some(v) => v.to_string_lossy().into_owned(), - None => "new-project".to_string(), - }; - - // Generate a random port to run the server on. - let serve_port = rand::thread_rng().gen_range(2000, 49151); - - // Configure the project with all of the values we know so far. - let source_project = SourceProject { - name, - serve_port, - partitions: HashMap::new(), - }; - let serialized = serde_json::to_string_pretty(&source_project).unwrap(); - - file.write(serialized.as_bytes()) - .map_err(|_| ProjectInitError::FailedToWrite)?; - - Ok(Project::from_source_project(source_project, project_path)) - } - - /// Attempts to load a project from the file named PROJECT_FILENAME from the - /// given folder. - pub fn load>(location: T) -> Result { - let project_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); - - fs::metadata(&project_path) - .map_err(|_| ProjectLoadError::DidNotExist(project_path.clone()))?; - - let mut file = File::open(&project_path) - .map_err(|_| ProjectLoadError::FailedToOpen(project_path.clone()))?; - - let mut contents = String::new(); - - file.read_to_string(&mut contents) - .map_err(|_| ProjectLoadError::FailedToRead(project_path.clone()))?; - - let source_project = serde_json::from_str(&contents) - .map_err(|e| ProjectLoadError::InvalidJson(project_path.clone(), e))?; - - Ok(Project::from_source_project(source_project, project_path)) - } - - /// Saves the given project file to the given folder with the appropriate name. - pub fn save>(&self, location: T) -> Result<(), ProjectSaveError> { - let project_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); - - let mut file = File::create(&project_path) - .map_err(|_| ProjectSaveError::FailedToCreate)?; - - let source_project = self.as_source_project(); - let serialized = serde_json::to_string_pretty(&source_project).unwrap(); - - file.write(serialized.as_bytes()).unwrap(); - - Ok(()) - } } + +#[derive(Debug)] +pub enum ProjectLoadExactError { + IoError(io::Error), + JsonError(serde_json::Error), +} + +impl fmt::Display for ProjectLoadExactError { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + match self { + ProjectLoadExactError::IoError(inner) => write!(output, "{}", inner), + ProjectLoadExactError::JsonError(inner) => write!(output, "{}", inner), + } + } +} + +#[derive(Debug)] +pub enum ProjectInitError {} + +impl fmt::Display for ProjectInitError { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + write!(output, "ProjectInitError") + } +} + +#[derive(Debug)] +pub enum ProjectLoadFuzzyError { + NotFound, + IoError(io::Error), + JsonError(serde_json::Error), +} + +impl From for ProjectLoadFuzzyError { + fn from(error: ProjectLoadExactError) -> ProjectLoadFuzzyError { + match error { + ProjectLoadExactError::IoError(inner) => ProjectLoadFuzzyError::IoError(inner), + ProjectLoadExactError::JsonError(inner) => ProjectLoadFuzzyError::JsonError(inner), + } + } +} + +impl fmt::Display for ProjectLoadFuzzyError { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + match self { + ProjectLoadFuzzyError::NotFound => write!(output, "Project not found."), + ProjectLoadFuzzyError::IoError(inner) => write!(output, "{}", inner), + ProjectLoadFuzzyError::JsonError(inner) => write!(output, "{}", inner), + } + } +} + +#[derive(Debug)] +pub enum ProjectSaveError {} + +impl fmt::Display for ProjectSaveError { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + write!(output, "ProjectSaveError") + } +} + +#[derive(Debug)] +pub enum ProjectNode { + Regular { + class_name: String, + children: HashMap, + + // ignore_unknown: bool, + }, + SyncPoint { + path: PathBuf, + }, +} + +#[derive(Debug)] +pub struct Project { + pub name: String, + pub tree: HashMap, + pub file_location: PathBuf, +} + +impl Project { + pub fn init(_project_folder_location: &Path) -> Result<(), ProjectInitError> { + unimplemented!(); + } + + pub fn locate(start_location: &Path) -> Option { + // 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 location_metadata.is_file() { + return Some(start_location.to_path_buf()); + } else if location_metadata.is_dir() { + let with_file = start_location.join(PROJECT_FILENAME); + + match fs::metadata(&with_file) { + Ok(with_file_metadata) => { + if with_file_metadata.is_file() { + return Some(with_file); + } else { + return None; + } + }, + Err(_) => {}, + } + } + + match start_location.parent() { + Some(parent_location) => Self::locate(parent_location), + None => None, + } + } + + pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result { + let project_path = Self::locate(fuzzy_project_location) + .ok_or(ProjectLoadFuzzyError::NotFound)?; + + Self::load_exact(&project_path).map_err(From::from) + } + + pub fn load_exact(project_file_location: &Path) -> Result { + let contents = fs::read_to_string(project_file_location) + .map_err(ProjectLoadExactError::IoError)?; + + let parsed: SourceProject = serde_json::from_str(&contents) + .map_err(ProjectLoadExactError::JsonError)?; + + Ok(parsed.into_project(project_file_location)) + } + + pub fn save(&self) -> Result<(), ProjectSaveError> { + let _source_project = self.to_source_project(); + + unimplemented!(); + } + + fn to_source_project(&self) -> SourceProject { + unimplemented!(); + } +} \ No newline at end of file diff --git a/server/src/rbx.rs b/server/src/rbx.rs index e3437f3c..d3873cb7 100644 --- a/server/src/rbx.rs +++ b/server/src/rbx.rs @@ -1,7 +1,6 @@ -use std::borrow::Cow; use std::collections::HashMap; -use id::Id; +use id::{Id, get_id}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] @@ -23,46 +22,107 @@ pub struct RbxInstance { /// Contains all other properties of an Instance. pub properties: HashMap, + /// The unique ID of the instance + id: Id, + /// All of the children of this instance. Order is relevant to preserve! - pub children: Vec, + children: Vec, /// The parent of the instance, if there is one. - pub parent: Option, + parent: Option, } -// This seems like a really bad idea? -// Why isn't there a blanket impl for this for all T? -impl<'a> From<&'a RbxInstance> for Cow<'a, RbxInstance> { - fn from(instance: &'a RbxInstance) -> Cow<'a, RbxInstance> { - Cow::Borrowed(instance) +impl RbxInstance { + pub fn get_id(&self) -> Id { + self.id + } +} + +pub struct Descendants<'a> { + tree: &'a RbxTree, + ids_to_visit: Vec, +} + +impl<'a> Iterator for Descendants<'a> { + type Item = &'a RbxInstance; + + fn next(&mut self) -> Option { + loop { + let id = match self.ids_to_visit.pop() { + Some(id) => id, + None => break, + }; + + match self.tree.get_instance(id) { + Some(instance) => { + for child_id in &instance.children { + self.ids_to_visit.push(*child_id); + } + + return Some(instance); + }, + None => continue, + } + } + + None } } pub struct RbxTree { instances: HashMap, + pub root_instance_id: Id, } impl RbxTree { pub fn new() -> RbxTree { + let root_instance_id = get_id(); + let root_instance = RbxInstance { + name: "game".to_string(), + class_name: "DataModel".to_string(), + properties: HashMap::new(), + id: root_instance_id, + children: Vec::new(), + parent: None, + }; + + let mut instances = HashMap::new(); + instances.insert(root_instance_id, root_instance); + RbxTree { - instances: HashMap::new(), + instances, + root_instance_id, } } + pub fn get_instance(&self, id: Id) -> Option<&RbxInstance> { + self.instances.get(&id) + } + pub fn get_all_instances(&self) -> &HashMap { &self.instances } - pub fn insert_instance(&mut self, id: Id, instance: RbxInstance) { - if let Some(parent_id) = instance.parent { - if let Some(mut parent) = self.instances.get_mut(&parent_id) { - if !parent.children.contains(&id) { - parent.children.push(id); + pub fn insert_instance(&mut self, mut instance: RbxInstance) { + match instance.parent { + Some(parent_id) => { + match self.instances.get_mut(&parent_id) { + Some(mut parent) => { + if !parent.children.contains(&instance.id) { + parent.children.push(instance.id); + } + }, + None => { + panic!("Tree consistency error, parent {} was not present in tree.", parent_id); + } } - } + }, + None => { + instance.parent = Some(self.root_instance_id); + }, } - self.instances.insert(id, instance); + self.instances.insert(instance.id, instance); } pub fn delete_instance(&mut self, id: Id) -> Vec { @@ -101,27 +161,20 @@ impl RbxTree { ids_deleted } - pub fn get_instance_and_descendants<'a, 'b, T>(&'a self, id: Id, output: &'b mut HashMap) - where T: From<&'a RbxInstance> - { - let mut ids_to_visit = vec![id]; - - loop { - let id = match ids_to_visit.pop() { - Some(id) => id, - None => break, - }; - - match self.instances.get(&id) { - Some(instance) => { - output.insert(id, instance.into()); - - for child_id in &instance.children { - ids_to_visit.push(*child_id); - } - }, - None => continue, - } + pub fn iter_descendants<'a>(&'a self, id: Id) -> Descendants<'a> { + match self.get_instance(id) { + Some(instance) => { + Descendants { + tree: self, + ids_to_visit: instance.children.clone(), + } + }, + None => { + Descendants { + tree: self, + ids_to_visit: vec![], + } + }, } } -} +} \ No newline at end of file diff --git a/server/src/rbx_session.rs b/server/src/rbx_session.rs deleted file mode 100644 index 0abd25e8..00000000 --- a/server/src/rbx_session.rs +++ /dev/null @@ -1,306 +0,0 @@ -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; - -use file_route::FileRoute; -use id::{Id, get_id}; -use message_session::{Message, MessageSession}; -use partition::Partition; -use project::Project; -use rbx::{RbxInstance, RbxTree, RbxValue}; -use vfs_session::{VfsSession, FileItem, FileChange}; - -static SERVICES: &'static [&'static str] = &[ - "Chat", - "Lighting", - "LocalizationService", - "Players", - "ReplicatedFirst", - "ReplicatedStorage", - "ServerScriptService", - "ServerStorage", - "SoundService", - "StarterGui", - "StarterPack", - "StarterPlayer", - "TestService", - "Workspace", -]; - -fn get_partition_target_class_name(target: &[String]) -> &'static str { - match target.len() { - 1 => { - let target_name = &target[0]; - - for &service in SERVICES { - if service == target_name { - return service; - } - } - - "Folder" - }, - 2 => { - "Folder" - }, - _ => "Folder", - } -} - -// TODO: Rethink data structure and insertion/update behavior. Maybe break some -// pieces off into a new object? -fn file_to_instances( - file_item: &FileItem, - partition: &Partition, - tree: &mut RbxTree, - instances_by_route: &mut HashMap, - parent_id: Option, -) -> (Id, Vec) { - match file_item { - FileItem::File { contents, route } => { - let primary_id = match instances_by_route.get(&file_item.get_route()) { - Some(&id) => id, - None => { - let id = get_id(); - instances_by_route.insert(route.clone(), id); - - id - }, - }; - - // This is placeholder logic; this whole function is! - let (class_name, property_key, name) = { - let file_name = match route.route.last() { - Some(v) => v.to_string(), - None => partition.path.file_name().unwrap().to_str().unwrap().to_string() - }; - - let use_partition_name = route.route.len() == 0; - - let partition_name = partition.target.last().unwrap(); - - fn strip_suffix<'a>(source: &'a str, suffix: &'static str) -> String { - source[..source.len() - suffix.len()].to_string() - } - - if file_name.ends_with(".client.lua") { - let name = if use_partition_name { - partition_name.clone() - } else { - strip_suffix(&file_name, ".client.lua") - }; - - ("LocalScript", "Source", name) - } else if file_name.ends_with(".server.lua") { - let name = if use_partition_name { - partition_name.clone() - } else { - strip_suffix(&file_name, ".server.lua") - }; - - ("Script", "Source", name) - } else if file_name.ends_with(".lua") { - let name = if use_partition_name { - partition_name.clone() - } else { - strip_suffix(&file_name, ".lua") - }; - - ("ModuleScript", "Source", name) - } else { - let name = if use_partition_name { - partition_name.clone() - } else { - file_name - }; - - // TODO: Error/warn/skip instead of falling back - ("StringValue", "Value", name) - } - }; - - let mut properties = HashMap::new(); - properties.insert(property_key.to_string(), RbxValue::String { value: contents.clone() }); - - tree.insert_instance(primary_id, RbxInstance { - name, - class_name: class_name.to_string(), - properties, - children: Vec::new(), - parent: parent_id, - }); - - (primary_id, vec![primary_id]) - }, - FileItem::Directory { children, route } => { - let primary_id = match instances_by_route.get(&file_item.get_route()) { - Some(&id) => id, - None => { - let id = get_id(); - instances_by_route.insert(route.clone(), id); - - id - }, - }; - - let mut child_ids = Vec::new(); - - let mut changed_ids = vec![primary_id]; - - for child_file_item in children.values() { - let (child_id, mut child_changed_ids) = file_to_instances(child_file_item, partition, tree, instances_by_route, Some(primary_id)); - - child_ids.push(child_id); - changed_ids.push(child_id); - - // TODO: Should I stop using drain on Vecs of Copyable types? - for id in child_changed_ids.drain(..) { - changed_ids.push(id); - } - } - - let class_name = get_partition_target_class_name(&route.route).to_string(); - - let name = if route.route.len() == 0 { - partition.target.last().unwrap().clone() - } else { - route.file_name(partition) - }; - - tree.insert_instance(primary_id, RbxInstance { - name, - class_name, - properties: HashMap::new(), - children: child_ids, - parent: parent_id, - }); - - (primary_id, changed_ids) - }, - } -} - -pub struct RbxSession { - project: Project, - - vfs_session: Arc>, - - message_session: MessageSession, - - /// The RbxInstance that represents each partition. - // TODO: Can this be removed in favor of instances_by_route? - pub partition_instances: HashMap, - - /// Keeps track of all of the instances in the tree - pub tree: RbxTree, - - /// A map from files in the VFS to instances loaded in the session. - instances_by_route: HashMap, -} - -impl RbxSession { - pub fn new(project: Project, vfs_session: Arc>, message_session: MessageSession) -> RbxSession { - RbxSession { - project, - vfs_session, - message_session, - partition_instances: HashMap::new(), - tree: RbxTree::new(), - instances_by_route: HashMap::new(), - } - } - - pub fn read_partitions(&mut self) { - let vfs_session_arc = self.vfs_session.clone(); - let vfs_session = vfs_session_arc.read().unwrap(); - - for partition in self.project.partitions.values() { - let route = FileRoute { - partition: partition.name.clone(), - route: Vec::new(), - }; - let file_item = vfs_session.get_by_route(&route).unwrap(); - - let parent_id = match route.parent() { - Some(parent_route) => match self.instances_by_route.get(&parent_route) { - Some(&parent_id) => Some(parent_id), - None => None, - }, - None => None, - }; - - let (root_id, _) = file_to_instances(file_item, partition, &mut self.tree, &mut self.instances_by_route, parent_id); - - self.partition_instances.insert(partition.name.clone(), root_id); - } - } - - pub fn handle_change(&mut self, change: &FileChange) { - let vfs_session_arc = self.vfs_session.clone(); - let vfs_session = vfs_session_arc.read().unwrap(); - - match change { - FileChange::Created(route) | FileChange::Updated(route) => { - let file_item = vfs_session.get_by_route(route).unwrap(); - let partition = self.project.partitions.get(&route.partition).unwrap(); - - let parent_id = match route.parent() { - Some(parent_route) => match self.instances_by_route.get(&parent_route) { - Some(&parent_id) => Some(parent_id), - None => None, - }, - None => None, - }; - - let (_, changed_ids) = file_to_instances(file_item, partition, &mut self.tree, &mut self.instances_by_route, parent_id); - - let messages = changed_ids - .iter() - .map(|&id| Message::InstanceChanged { id }) - .collect::>(); - - self.message_session.push_messages(&messages); - }, - FileChange::Deleted(route) => { - match self.instances_by_route.get(route) { - Some(&id) => { - self.tree.delete_instance(id); - self.instances_by_route.remove(route); - self.message_session.push_messages(&[Message::InstanceChanged { id }]); - }, - None => (), - } - }, - FileChange::Moved(from_route, to_route) => { - let mut messages = Vec::new(); - - match self.instances_by_route.get(from_route) { - Some(&id) => { - self.tree.delete_instance(id); - self.instances_by_route.remove(from_route); - messages.push(Message::InstanceChanged { id }); - }, - None => (), - } - - let file_item = vfs_session.get_by_route(to_route).unwrap(); - let partition = self.project.partitions.get(&to_route.partition).unwrap(); - - let parent_id = match to_route.parent() { - Some(parent_route) => match self.instances_by_route.get(&parent_route) { - Some(&parent_id) => Some(parent_id), - None => None, - }, - None => None, - }; - - let (_, changed_ids) = file_to_instances(file_item, partition, &mut self.tree, &mut self.instances_by_route, parent_id); - - for id in changed_ids { - messages.push(Message::InstanceChanged { id }); - } - - self.message_session.push_messages(&messages); - }, - } - } -} diff --git a/server/src/session.rs b/server/src/session.rs index 55341bb3..b1e9c7c8 100644 --- a/server/src/session.rs +++ b/server/src/session.rs @@ -1,95 +1,121 @@ -use std::sync::{mpsc, Arc, RwLock}; -use std::thread; +use std::{ + sync::{Arc, RwLock, Mutex, mpsc}, + thread, + io, + time::Duration, +}; -use message_session::MessageSession; -use partition_watcher::PartitionWatcher; -use project::Project; -use rbx_session::RbxSession; -use vfs_session::VfsSession; +use rand; -/// Stub trait for middleware -trait Middleware { -} +use notify::{ + self, + DebouncedEvent, + RecommendedWatcher, + RecursiveMode, + Watcher, +}; + +use ::{ + message_queue::MessageQueue, + rbx::RbxTree, + project::{Project, ProjectNode}, + vfs::Vfs, +}; + +const WATCH_TIMEOUT_MS: u64 = 100; pub struct Session { - pub project: Project, - vfs_session: Arc>, - rbx_session: Arc>, - message_session: MessageSession, - watchers: Vec, + project: Project, + pub session_id: String, + pub message_queue: Arc, + pub tree: Arc>, + vfs: Arc>, + watchers: Vec, } impl Session { pub fn new(project: Project) -> Session { - let message_session = MessageSession::new(); - let vfs_session = Arc::new(RwLock::new(VfsSession::new(project.clone()))); - let rbx_session = Arc::new(RwLock::new(RbxSession::new(project.clone(), vfs_session.clone(), message_session.clone()))); + let session_id = rand::random::().to_string(); Session { - vfs_session, - rbx_session, - watchers: Vec::new(), - message_session, + session_id, project, + message_queue: Arc::new(MessageQueue::new()), + tree: Arc::new(RwLock::new(RbxTree::new())), + vfs: Arc::new(Mutex::new(Vfs::new())), + watchers: Vec::new(), } } - pub fn start(&mut self) { - { - let mut vfs_session = self.vfs_session.write().unwrap(); - vfs_session.read_partitions(); - } - - { - let mut rbx_session = self.rbx_session.write().unwrap(); - rbx_session.read_partitions(); - } - - let (tx, rx) = mpsc::channel(); - - for partition in self.project.partitions.values() { - let watcher = PartitionWatcher::start_new(partition.clone(), tx.clone()); - - self.watchers.push(watcher); - } - - { - let vfs_session = self.vfs_session.clone(); - let rbx_session = self.rbx_session.clone(); - - thread::spawn(move || { - loop { - match rx.recv() { - Ok(change) => { - { - let mut vfs_session = vfs_session.write().unwrap(); - vfs_session.handle_change(&change); - } - - { - let mut rbx_session = rbx_session.write().unwrap(); - rbx_session.handle_change(&change); - } - }, - Err(_) => break, + pub fn start(&mut self) -> io::Result<()> { + fn add_sync_points(vfs: &mut Vfs, project_node: &ProjectNode) -> io::Result<()> { + match project_node { + ProjectNode::Regular { children, .. } => { + for child in children.values() { + add_sync_points(vfs, child)?; } - } - }); + }, + ProjectNode::SyncPoint { path } => { + vfs.add_root(path)?; + }, + } + + Ok(()) } + + { + let mut vfs = self.vfs.lock().unwrap(); + + for child in self.project.tree.values() { + add_sync_points(&mut vfs, child)?; + } + + for root in vfs.get_roots() { + println!("Watching {}", root.display()); + + let (watch_tx, watch_rx) = mpsc::channel(); + + let mut watcher = notify::watcher(watch_tx, Duration::from_millis(WATCH_TIMEOUT_MS)).unwrap(); + + watcher.watch(root, RecursiveMode::Recursive).unwrap(); + self.watchers.push(watcher); + + let vfs = Arc::clone(&self.vfs); + + thread::spawn(move || { + println!("Thread started"); + loop { + match watch_rx.recv() { + Ok(event) => { + match event { + DebouncedEvent::Create(path) | DebouncedEvent::Write(path) => { + let mut vfs = vfs.lock().unwrap(); + vfs.add_or_update(&path).unwrap(); + }, + DebouncedEvent::Remove(path) => { + let mut vfs = vfs.lock().unwrap(); + vfs.remove(&path); + }, + DebouncedEvent::Rename(from_path, to_path) => { + let mut vfs = vfs.lock().unwrap(); + vfs.remove(&from_path); + vfs.add_or_update(&to_path).unwrap(); + }, + _ => continue, + }; + }, + Err(_) => break, + }; + } + println!("Thread stopped"); + }); + } + } + + Ok(()) } - pub fn stop(self) { + pub fn get_project(&self) -> &Project { + &self.project } - - pub fn get_vfs_session(&self) -> Arc> { - self.vfs_session.clone() - } - - pub fn get_rbx_session(&self) -> Arc> { - self.rbx_session.clone() - } - - pub fn get_message_session(&self) -> MessageSession { - self.message_session.clone() - } -} +} \ No newline at end of file diff --git a/server/src/vfs.rs b/server/src/vfs.rs new file mode 100644 index 00000000..7ac6e0f4 --- /dev/null +++ b/server/src/vfs.rs @@ -0,0 +1,139 @@ +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, + fs, + io, +}; + +#[derive(Debug)] +pub struct Vfs { + contents: HashMap>, + items: HashMap, + roots: HashSet, +} + +impl Vfs { + pub fn new() -> Vfs { + Vfs { + contents: HashMap::new(), + items: HashMap::new(), + roots: HashSet::new(), + } + } + + pub fn add_root<'a, 'b>(&'a mut self, root_path: &'b Path) -> io::Result<&'a VfsItem> { + debug_assert!(root_path.is_absolute()); + + self.roots.insert(root_path.to_path_buf()); + + VfsItem::get(self, root_path) + } + + pub fn get_roots(&self) -> &HashSet { + &self.roots + } + + pub fn get(&mut self, path: &Path) -> Option<&VfsItem> { + debug_assert!(path.is_absolute()); + debug_assert!(self.is_valid_path(path)); + + self.items.get(path) + } + + pub fn remove(&mut self, path: &Path) { + debug_assert!(path.is_absolute()); + debug_assert!(self.is_valid_path(path)); + + match self.items.remove(path) { + Some(item) => match item { + VfsItem::File(_) => { + self.contents.remove(path); + }, + VfsItem::Directory(VfsDirectory { children, .. }) => { + for child_path in &children { + self.remove(child_path); + } + }, + }, + None => {}, + } + } + + pub fn add_or_update<'a, 'b>(&'a mut self, path: &'b Path) -> io::Result<&'a VfsItem> { + debug_assert!(path.is_absolute()); + debug_assert!(self.is_valid_path(path)); + + VfsItem::get(self, path) + } + + fn is_valid_path(&self, path: &Path) -> bool { + let mut is_valid_path = false; + + for root_path in &self.roots { + if path.starts_with(root_path) { + is_valid_path = true; + break; + } + } + + is_valid_path + } +} + +#[derive(Debug)] +pub struct VfsFile { + path: PathBuf, +} + +#[derive(Debug)] +pub struct VfsDirectory { + path: PathBuf, + children: HashSet, +} + +#[derive(Debug)] +pub enum VfsItem { + File(VfsFile), + Directory(VfsDirectory), +} + +impl VfsItem { + fn get<'a, 'b>(vfs: &'a mut Vfs, root_path: &'b Path) -> io::Result<&'a VfsItem> { + let metadata = fs::metadata(root_path)?; + + if metadata.is_file() { + let item = VfsItem::File(VfsFile { + path: root_path.to_path_buf(), + }); + + vfs.items.insert(root_path.to_path_buf(), item); + + let contents = fs::read(root_path)?; + vfs.contents.insert(root_path.to_path_buf(), contents); + + Ok(vfs.items.get(root_path).unwrap()) + } else if metadata.is_dir() { + let mut children = HashSet::new(); + + for entry in fs::read_dir(root_path)? { + let entry = entry?; + let path = entry.path(); + + VfsItem::get(vfs, &path)?; + + children.insert(path); + } + + let item = VfsItem::Directory(VfsDirectory { + path: root_path.to_path_buf(), + children, + }); + + vfs.items.insert(root_path.to_path_buf(), item); + + Ok(vfs.items.get(root_path).unwrap()) + } else { + unimplemented!(); + } + } +} \ No newline at end of file diff --git a/server/src/vfs_session.rs b/server/src/vfs_session.rs deleted file mode 100644 index c4dc3e0b..00000000 --- a/server/src/vfs_session.rs +++ /dev/null @@ -1,242 +0,0 @@ -use std::collections::HashMap; -use std::io::Read; -use std::fs::{self, File}; -use std::mem; - -use file_route::FileRoute; -use project::Project; - -/// Represents a file or directory that has been read from the filesystem. -#[derive(Debug, Clone)] -pub enum FileItem { - File { - contents: String, - route: FileRoute, - }, - Directory { - children: HashMap, - route: FileRoute, - }, -} - -impl FileItem { - pub fn get_route(&self) -> &FileRoute { - match self { - FileItem::File { route, .. } => route, - FileItem::Directory { route, .. } => route, - } - } -} - -#[derive(Debug, Clone)] -pub enum FileChange { - Created(FileRoute), - Deleted(FileRoute), - Updated(FileRoute), - Moved(FileRoute, FileRoute), -} - -pub struct VfsSession { - pub project: Project, - - /// The in-memory files associated with each partition. - pub partition_files: HashMap, -} - -impl VfsSession { - pub fn new(project: Project) -> VfsSession { - VfsSession { - project, - partition_files: HashMap::new(), - } - } - - pub fn read_partitions(&mut self) { - for partition_name in self.project.partitions.keys() { - let route = FileRoute { - partition: partition_name.clone(), - route: Vec::new(), - }; - - let file_item = self.read(&route).expect("Couldn't load partitions"); - - self.partition_files.insert(partition_name.clone(), file_item); - } - } - - pub fn handle_change(&mut self, change: &FileChange) -> Option<()> { - match change { - FileChange::Created(route) | FileChange::Updated(route) => { - let new_item = self.read(&route).ok()?; - self.set_file_item(new_item); - }, - FileChange::Deleted(route) => { - self.delete_route(&route); - }, - FileChange::Moved(from_route, to_route) => { - let new_item = self.read(&to_route).ok()?; - self.delete_route(&from_route); - self.set_file_item(new_item); - }, - } - - None - } - - pub fn get_by_route(&self, route: &FileRoute) -> Option<&FileItem> { - let partition = self.partition_files.get(&route.partition)?; - let mut current = partition; - - for piece in &route.route { - match current { - FileItem::File { .. } => return None, - FileItem::Directory { children, .. } => { - current = children.get(piece)?; - }, - } - } - - Some(current) - } - - pub fn get_by_route_mut(&mut self, route: &FileRoute) -> Option<&mut FileItem> { - let mut current = self.partition_files.get_mut(&route.partition)?; - - for piece in &route.route { - let mut next = match { current } { - FileItem::File { .. } => return None, - FileItem::Directory { children, .. } => { - children.get_mut(piece)? - }, - }; - - current = next; - } - - Some(current) - } - - pub fn set_file_item(&mut self, item: FileItem) { - match self.get_by_route_mut(item.get_route()) { - Some(existing) => { - mem::replace(existing, item); - return; - }, - None => {}, - } - - if item.get_route().route.len() > 0 { - let mut parent_route = item.get_route().clone(); - let child_name = parent_route.route.pop().unwrap(); - - let mut parent_children = HashMap::new(); - parent_children.insert(child_name, item); - - let parent_item = FileItem::Directory { - route: parent_route, - children: parent_children, - }; - - self.set_file_item(parent_item); - } else { - self.partition_files.insert(item.get_route().partition.clone(), item); - } - } - - pub fn delete_route(&mut self, route: &FileRoute) -> Option<()> { - if route.route.len() == 0 { - self.partition_files.remove(&route.partition); - return Some(()); - } - - let mut current = self.partition_files.get_mut(&route.partition)?; - - for i in 0..(route.route.len() - 1) { - let piece = &route.route[i]; - - let mut next = match { current } { - FileItem::File { .. } => return None, - FileItem::Directory { children, .. } => { - children.get_mut(piece)? - }, - }; - - current = next; - } - - match current { - FileItem::Directory { children, .. } => { - children.remove(route.route.last().unwrap().as_str()); - }, - _ => {}, - } - - Some(()) - } - - fn read(&self, route: &FileRoute) -> Result { - let partition_path = &self.project.partitions.get(&route.partition) - .ok_or(())?.path; - let path = route.to_path_buf(partition_path); - - let metadata = fs::metadata(path) - .map_err(|_| ())?; - - if metadata.is_dir() { - self.read_directory(route) - } else if metadata.is_file() { - self.read_file(route) - } else { - Err(()) - } - } - - fn read_file(&self, route: &FileRoute) -> Result { - let partition_path = &self.project.partitions.get(&route.partition) - .ok_or(())?.path; - let path = route.to_path_buf(partition_path); - - let mut file = File::open(path) - .map_err(|_| ())?; - - let mut contents = String::new(); - - file.read_to_string(&mut contents) - .map_err(|_| ())?; - - Ok(FileItem::File { - contents, - route: route.clone(), - }) - } - - fn read_directory(&self, route: &FileRoute) -> Result { - let partition_path = &self.project.partitions.get(&route.partition) - .ok_or(())?.path; - let path = route.to_path_buf(partition_path); - - let reader = fs::read_dir(path) - .map_err(|_| ())?; - - let mut children = HashMap::new(); - - for entry in reader { - let entry = entry - .map_err(|_| ())?; - - let path = entry.path(); - let name = path.file_name().unwrap().to_string_lossy().into_owned(); - - let child_route = route.extended_with(&[&name]); - - let child_item = self.read(&child_route)?; - - children.insert(name, child_item); - } - - Ok(FileItem::Directory { - children, - route: route.clone(), - }) - } -} diff --git a/server/src/web.rs b/server/src/web.rs index 2a5fefd4..5d312aa4 100644 --- a/server/src/web.rs +++ b/server/src/web.rs @@ -1,197 +1,152 @@ -use std::borrow::Cow; -use std::collections::HashMap; -use std::sync::{mpsc, RwLock, Arc}; +use std::{ + borrow::Cow, + collections::HashMap, + sync::{mpsc, Arc}, +}; use rouille::{self, Request, Response}; -use id::Id; -use message_session::{MessageSession, Message}; -use project::Project; -use rbx::RbxInstance; -use rbx_session::RbxSession; -use session::Session; - -/// The set of configuration the web server needs to start. -pub struct WebConfig { - pub port: u64, - pub project: Project, - pub server_id: u64, - pub rbx_session: Arc>, - pub message_session: MessageSession, -} - -impl WebConfig { - pub fn from_session(server_id: u64, port: u64, session: &Session) -> WebConfig { - WebConfig { - port, - server_id, - project: session.project.clone(), - rbx_session: session.get_rbx_session(), - message_session: session.get_message_session(), - } - } -} +use ::{ + id::Id, + message_queue::Message, + project::Project, + rbx::RbxInstance, + session::Session, +}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ServerInfoResponse<'a> { - pub server_id: &'a str, + pub session_id: &'a str, pub server_version: &'a str, pub protocol_version: u64, - pub partitions: HashMap>, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReadAllResponse<'a> { - pub server_id: &'a str, - pub message_cursor: i32, - pub instances: Cow<'a, HashMap>, - pub partition_instances: Cow<'a, HashMap>, + pub root_instance_id: Id, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ReadResponse<'a> { - pub server_id: &'a str, - pub message_cursor: i32, + pub session_id: &'a str, + pub message_cursor: u32, pub instances: HashMap>, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubscribeResponse<'a> { - pub server_id: &'a str, - pub message_cursor: i32, + pub session_id: &'a str, + pub message_cursor: u32, pub messages: Cow<'a, [Message]>, } pub struct Server { - config: WebConfig, + session: Arc, server_version: &'static str, - server_id: String, } impl Server { - pub fn new(config: WebConfig) -> Server { + pub fn new(session: Arc) -> Server { Server { + session: session, server_version: env!("CARGO_PKG_VERSION"), - server_id: config.server_id.to_string(), - config, } } + #[allow(unreachable_code)] pub fn handle_request(&self, request: &Request) -> Response { router!(request, (GET) (/) => { - Response::text("Rojo up and running!") + Response::text("Rojo is up and running!") }, (GET) (/api/rojo) => { // Get a summary of information about the server. - let mut partitions = HashMap::new(); - - for partition in self.config.project.partitions.values() { - partitions.insert(partition.name.clone(), partition.target.clone()); - } + let tree = self.session.tree.read().unwrap(); Response::json(&ServerInfoResponse { server_version: self.server_version, protocol_version: 2, - server_id: &self.server_id, - partitions: partitions, + session_id: &self.session.session_id, + root_instance_id: tree.root_instance_id, }) }, - (GET) (/api/subscribe/{ cursor: i32 }) => { + (GET) (/api/subscribe/{ cursor: u32 }) => { // Retrieve any messages past the given cursor index, and if // there weren't any, subscribe to receive any new messages. + let message_queue = Arc::clone(&self.session.message_queue); + // Did the client miss any messages since the last subscribe? { - let messages = self.config.message_session.messages.read().unwrap(); + let (new_cursor, new_messages) = message_queue.get_messages_since(cursor); - if cursor > messages.len() as i32 { + if new_messages.len() > 0 { return Response::json(&SubscribeResponse { - server_id: &self.server_id, + session_id: &self.session.session_id, messages: Cow::Borrowed(&[]), - message_cursor: messages.len() as i32 - 1, - }); - } - - if cursor < messages.len() as i32 - 1 { - let new_messages = &messages[(cursor + 1) as usize..]; - let new_cursor = cursor + new_messages.len() as i32; - - return Response::json(&SubscribeResponse { - server_id: &self.server_id, - messages: Cow::Borrowed(new_messages), message_cursor: new_cursor, - }); + }) } } let (tx, rx) = mpsc::channel(); - let sender_id = self.config.message_session.subscribe(tx); + let sender_id = message_queue.subscribe(tx); match rx.recv() { Ok(_) => (), Err(_) => return Response::text("error!").with_status_code(500), } - self.config.message_session.unsubscribe(sender_id); + message_queue.unsubscribe(sender_id); { - let messages = self.config.message_session.messages.read().unwrap(); - let new_messages = &messages[(cursor + 1) as usize..]; - let new_cursor = cursor + new_messages.len() as i32; + let (new_cursor, new_messages) = message_queue.get_messages_since(cursor); - Response::json(&SubscribeResponse { - server_id: &self.server_id, - messages: Cow::Borrowed(new_messages), + return Response::json(&SubscribeResponse { + session_id: &self.session.session_id, + messages: Cow::Owned(new_messages), message_cursor: new_cursor, }) } }, - (GET) (/api/read_all) => { - let rbx_session = self.config.rbx_session.read().unwrap(); - - let message_cursor = self.config.message_session.get_message_cursor(); - - Response::json(&ReadAllResponse { - server_id: &self.server_id, - message_cursor, - instances: Cow::Borrowed(rbx_session.tree.get_all_instances()), - partition_instances: Cow::Borrowed(&rbx_session.partition_instances), - }) - }, - (GET) (/api/read/{ id_list: String }) => { - let requested_ids = id_list + let message_queue = Arc::clone(&self.session.message_queue); + + let requested_ids: Result, _> = id_list .split(",") - .map(str::parse::) - .collect::, _>>(); + .map(str::parse) + .collect(); let requested_ids = match requested_ids { - Ok(v) => v, + Ok(id) => id, Err(_) => return rouille::Response::text("Malformed ID list").with_status_code(400), }; - let rbx_session = self.config.rbx_session.read().unwrap(); + let tree = self.session.tree.read().unwrap(); - let message_cursor = self.config.message_session.get_message_cursor(); + let message_cursor = message_queue.get_message_cursor(); let mut instances = HashMap::new(); - for requested_id in &requested_ids { - rbx_session.tree.get_instance_and_descendants(*requested_id, &mut instances); + for &requested_id in &requested_ids { + match tree.get_instance(requested_id) { + Some(instance) => { + instances.insert(instance.get_id(), Cow::Borrowed(instance)); + + for descendant in tree.iter_descendants(requested_id) { + instances.insert(descendant.get_id(), Cow::Borrowed(descendant)); + } + }, + None => {}, + } } Response::json(&ReadResponse { - server_id: &self.server_id, + session_id: &self.session.session_id, message_cursor, instances, }) @@ -200,13 +155,10 @@ impl Server { _ => Response::empty_404() ) } -} -/// Start the Rojo web server, taking over the current thread. -#[allow(unreachable_code)] -pub fn start(config: WebConfig) { - let address = format!("localhost:{}", config.port); - let server = Server::new(config); + pub fn listen(self, port: u64) { + let address = format!("localhost:{}", port); - rouille::start_server(address, move |request| server.handle_request(request)); -} + rouille::start_server(address, move |request| self.handle_request(request)); + } +} \ No newline at end of file diff --git a/server/src/web_util.rs b/server/src/web_util.rs index 0c344e6c..a6aea450 100644 --- a/server/src/web_util.rs +++ b/server/src/web_util.rs @@ -40,4 +40,4 @@ where { let body = read_json_text(&request)?; serde_json::from_str(&body).ok()? -} +} \ No newline at end of file diff --git a/server/tests/read_projects.rs b/server/tests/read_projects.rs new file mode 100644 index 00000000..05a85759 --- /dev/null +++ b/server/tests/read_projects.rs @@ -0,0 +1,36 @@ +#[macro_use] extern crate lazy_static; + +extern crate librojo; + +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use librojo::{ + project::Project, +}; + +lazy_static! { + static ref TEST_PROJECTS_ROOT: PathBuf = { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../test-projects") + }; +} + +#[test] +fn foo() { + let project_file_location = TEST_PROJECTS_ROOT.join("foo.json"); + let project = Project::load_exact(&project_file_location).unwrap(); + + assert_eq!(project.name, "foo"); + assert_eq!(project.tree.len(), 1); +} + +#[test] +fn empty() { + let project_file_location = TEST_PROJECTS_ROOT.join("empty/roblox-project.json"); + let project = Project::load_exact(&project_file_location).unwrap(); + + assert_eq!(project.name, "empty"); + assert_eq!(project.tree.len(), 0); +} \ No newline at end of file diff --git a/server/tests/web.rs b/server/tests/web.rs deleted file mode 100644 index c9ae6259..00000000 --- a/server/tests/web.rs +++ /dev/null @@ -1,483 +0,0 @@ -#[macro_use] extern crate lazy_static; - -extern crate rouille; -extern crate serde_json; -extern crate serde; -extern crate tempfile; -extern crate walkdir; - -extern crate librojo; - -mod test_util; -use test_util::*; - -use std::borrow::Cow; -use std::collections::HashMap; -use std::fs::{File, remove_file}; -use std::io::Write; -use std::path::PathBuf; - -use librojo::{ - session::Session, - project::Project, - web::{Server, WebConfig, ServerInfoResponse, ReadResponse, ReadAllResponse, SubscribeResponse}, - rbx::RbxValue, -}; - -lazy_static! { - static ref TEST_PROJECTS_ROOT: PathBuf = { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("../test-projects"); - path - }; -} - -#[test] -fn empty() { - let original_project_path = TEST_PROJECTS_ROOT.join("empty"); - - let project_tempdir = tempfile::tempdir().unwrap(); - let project_path = project_tempdir.path(); - - copy_recursive(&original_project_path, &project_path).unwrap(); - - let project = Project::load(&project_path).unwrap(); - let mut session = Session::new(project.clone()); - session.start(); - - let web_config = WebConfig::from_session(0, project.serve_port, &session); - let server = Server::new(web_config); - - { - let body = server.get_string("/api/rojo"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.protocol_version, 2); - assert_eq!(response.partitions.len(), 0); - } - - { - let body = server.get_string("/api/read_all"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, -1); - assert_eq!(response.instances.len(), 0); - } - - { - let body = server.get_string("/api/read/0"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, -1); - assert_eq!(response.instances.len(), 0); - } -} - -#[test] -fn one_partition() { - let original_project_path = TEST_PROJECTS_ROOT.join("one-partition"); - - let project_tempdir = tempfile::tempdir().unwrap(); - let project_path = project_tempdir.path(); - - copy_recursive(&original_project_path, &project_path).unwrap(); - - let project = Project::load(&project_path).unwrap(); - let mut session = Session::new(project.clone()); - session.start(); - - let web_config = WebConfig::from_session(0, project.serve_port, &session); - let server = Server::new(web_config); - - { - let body = server.get_string("/api/rojo"); - let response = serde_json::from_str::(&body).unwrap(); - - let mut partitions = HashMap::new(); - partitions.insert("lib".to_string(), vec!["ReplicatedStorage".to_string(), "OnePartition".to_string()]); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.protocol_version, 2); - assert_eq!(response.partitions, partitions); - } - - let initial_body = server.get_string("/api/read_all"); - let initial_response = { - let response = serde_json::from_str::(&initial_body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, -1); - assert_eq!(response.instances.len(), 4); - - let partition_id = *response.partition_instances.get("lib").unwrap(); - - let mut root_id = None; - let mut module_id = None; - let mut client_id = None; - let mut server_id = None; - - for (id, instance) in response.instances.iter() { - match (instance.name.as_str(), instance.class_name.as_str()) { - // TOOD: Should partition roots (and other directories) be some - // magical object instead of Folder? - // TODO: Should this name actually equal the last part of the - // partition's target? - ("OnePartition", "Folder") => { - assert!(root_id.is_none()); - root_id = Some(*id); - - assert_eq!(*id, partition_id); - - assert_eq!(instance.properties.len(), 0); - assert_eq!(instance.parent, None); - assert_eq!(instance.children.len(), 3); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("a", "ModuleScript") => { - assert!(module_id.is_none()); - module_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- a.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("b", "LocalScript") => { - assert!(client_id.is_none()); - client_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- b.client.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("a", "Script") => { - assert!(server_id.is_none()); - server_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- a.server.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - _ => { - panic!("Unexpected instance named {} of class {}", instance.name, instance.class_name); - }, - } - } - - let root_id = root_id.unwrap(); - let module_id = module_id.unwrap(); - let client_id = client_id.unwrap(); - let server_id = server_id.unwrap(); - - { - let root_instance = response.instances.get(&root_id).unwrap(); - - assert!(root_instance.children.contains(&module_id)); - assert!(root_instance.children.contains(&client_id)); - assert!(root_instance.children.contains(&server_id)); - } - - response - }; - - { - let temp_name = project_path.join("lib/c.client.lua"); - - { - let mut file = File::create(&temp_name).unwrap(); - file.write_all(b"-- c.client.lua").unwrap(); - } - - { - // Block until Rojo detects the addition of our temp file - let body = server.get_string("/api/subscribe/-1"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 0); - assert_eq!(response.messages.len(), 1); - - // TODO: Read which instance was changed and try to access it with - // /read - } - - let body = server.get_string("/api/read_all"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 0); - assert_eq!(response.instances.len(), 5); - - let partition_id = *response.partition_instances.get("lib").unwrap(); - - let mut root_id = None; - let mut module_id = None; - let mut client_id = None; - let mut server_id = None; - let mut new_id = None; - - for (id, instance) in response.instances.iter() { - match (instance.name.as_str(), instance.class_name.as_str()) { - // TOOD: Should partition roots (and other directories) be some - // magical object instead of Folder? - // TODO: Should this name actually equal the last part of the - // partition's target? - ("OnePartition", "Folder") => { - assert!(root_id.is_none()); - root_id = Some(*id); - - assert_eq!(*id, partition_id); - - assert_eq!(instance.properties.len(), 0); - assert_eq!(instance.parent, None); - assert_eq!(instance.children.len(), 4); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("a", "ModuleScript") => { - assert!(module_id.is_none()); - module_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- a.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("b", "LocalScript") => { - assert!(client_id.is_none()); - client_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- b.client.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("a", "Script") => { - assert!(server_id.is_none()); - server_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- a.server.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - ("c", "LocalScript") => { - assert!(new_id.is_none()); - new_id = Some(*id); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: "-- c.client.lua".to_string() }); - - assert_eq!(instance.properties, properties); - assert_eq!(instance.parent, Some(partition_id)); - assert_eq!(instance.children.len(), 0); - - let single_body = server.get_string(&format!("/api/read/{}", id)); - let single_response = serde_json::from_str::(&single_body).unwrap(); - - let single_instance = single_response.instances.get(id).unwrap(); - - assert_eq!(single_instance, &Cow::Borrowed(instance)); - }, - _ => {}, - } - } - - let root_id = root_id.unwrap(); - let module_id = module_id.unwrap(); - let client_id = client_id.unwrap(); - let server_id = server_id.unwrap(); - let new_id = new_id.unwrap(); - - let root_instance = response.instances.get(&root_id).unwrap(); - - assert!(root_instance.children.contains(&module_id)); - assert!(root_instance.children.contains(&client_id)); - assert!(root_instance.children.contains(&server_id)); - assert!(root_instance.children.contains(&new_id)); - - remove_file(&temp_name).unwrap(); - } - - { - // Block until Rojo detects the removal of our temp file - let body = server.get_string("/api/subscribe/0"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 1); - assert_eq!(response.messages.len(), 1); - } - - { - // Everything should be back to the initial state! - let body = server.get_string("/api/read_all"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 1); - assert_eq!(response.instances.len(), 4); - - assert_eq!(response.instances, initial_response.instances); - } - - // TODO: Test to change existing instance -} - -#[test] -fn partition_to_file() { - let original_project_path = TEST_PROJECTS_ROOT.join("partition-to-file"); - - let project_tempdir = tempfile::tempdir().unwrap(); - let project_path = project_tempdir.path(); - - copy_recursive(&original_project_path, &project_path).unwrap(); - - let project = Project::load(&project_path).unwrap(); - let mut session = Session::new(project.clone()); - session.start(); - - let web_config = WebConfig::from_session(0, project.serve_port, &session); - let server = Server::new(web_config); - - { - let body = server.get_string("/api/rojo"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.protocol_version, 2); - assert_eq!(response.partitions.len(), 1); - } - - { - let body = server.get_string("/api/read_all"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, -1); - assert_eq!(response.instances.len(), 1); - - let (id, instance) = response.instances.iter().next().unwrap(); - - assert_eq!(instance.name, "bar"); - assert_eq!(instance.class_name, "ModuleScript"); - assert_eq!(instance.properties.get("Source"), Some(&RbxValue::String { value: "-- foo.lua".to_string() })); - assert_eq!(instance.children.len(), 0); - assert_eq!(instance.parent, None); - - let body = server.get_string(&format!("/api/read/{}", id)); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, -1); - assert_eq!(response.instances.len(), 1); - - let single_instance = response.instances.values().next().unwrap(); - - assert_eq!(&Cow::Borrowed(instance), single_instance); - } - - let file_path = project_path.join("foo.lua"); - - { - let mut file = File::create(file_path).unwrap(); - file.write_all(b"-- modified").unwrap(); - } - - { - // Block until Rojo detects our file being modified - let body = server.get_string("/api/subscribe/-1"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 0); - assert_eq!(response.messages.len(), 1); - } - - { - let body = server.get_string("/api/read/0"); - let response = serde_json::from_str::(&body).unwrap(); - - assert_eq!(response.server_id, "0"); - assert_eq!(response.message_cursor, 0); - assert_eq!(response.instances.len(), 1); - - let instance = response.instances.values().next().unwrap(); - - assert_eq!(instance.name, "bar"); - assert_eq!(instance.class_name, "ModuleScript"); - assert_eq!(instance.properties.get("Source"), Some(&RbxValue::String { value: "-- modified".to_string() })); - assert_eq!(instance.children.len(), 0); - assert_eq!(instance.parent, None); - } -} diff --git a/test-projects/empty/roblox-project.json b/test-projects/empty/roblox-project.json new file mode 100644 index 00000000..5844e00e --- /dev/null +++ b/test-projects/empty/roblox-project.json @@ -0,0 +1,4 @@ +{ + "name": "empty", + "tree": {} +} \ No newline at end of file diff --git a/test-projects/empty/rojo.json b/test-projects/empty/rojo.json deleted file mode 100644 index 51c7917c..00000000 --- a/test-projects/empty/rojo.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "empty", - "servePort": 23456, - "partitions": {} -} \ No newline at end of file diff --git a/test-projects/foo.json b/test-projects/foo.json new file mode 100644 index 00000000..f12be6a8 --- /dev/null +++ b/test-projects/foo.json @@ -0,0 +1,26 @@ +{ + "name": "foo", + "tree": { + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "Rojo": { + "$className": "Folder", + "Plugin": { + "$path": "lib" + }, + "Roact": { + "$path": "modules/roact/lib" + }, + "Rodux": { + "$path": "modules/rodux/lib" + }, + "RoactRodux": { + "$path": "modules/roact-rodux/lib" + }, + "Promise": { + "$path": "modules/promise/lib" + } + } + } + } +} \ No newline at end of file diff --git a/test-projects/one-partition/lib/a.lua b/test-projects/one-partition/lib/a.lua deleted file mode 100644 index f23fd06a..00000000 --- a/test-projects/one-partition/lib/a.lua +++ /dev/null @@ -1 +0,0 @@ --- a.lua \ No newline at end of file diff --git a/test-projects/one-partition/lib/a.server.lua b/test-projects/one-partition/lib/a.server.lua deleted file mode 100644 index 08491e94..00000000 --- a/test-projects/one-partition/lib/a.server.lua +++ /dev/null @@ -1 +0,0 @@ --- a.server.lua \ No newline at end of file diff --git a/test-projects/one-partition/lib/b.client.lua b/test-projects/one-partition/lib/b.client.lua deleted file mode 100644 index 12d034fa..00000000 --- a/test-projects/one-partition/lib/b.client.lua +++ /dev/null @@ -1 +0,0 @@ --- b.client.lua \ No newline at end of file diff --git a/test-projects/one-partition/rojo.json b/test-projects/one-partition/rojo.json deleted file mode 100644 index d774a8f8..00000000 --- a/test-projects/one-partition/rojo.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "one-partition", - "servePort": 23456, - "partitions": { - "lib": { - "path": "lib", - "target": "ReplicatedStorage.OnePartition" - } - } -} \ No newline at end of file diff --git a/test-projects/partition-to-file/foo.lua b/test-projects/partition-to-file/foo.lua deleted file mode 100644 index 4c3ac65c..00000000 --- a/test-projects/partition-to-file/foo.lua +++ /dev/null @@ -1 +0,0 @@ --- foo.lua \ No newline at end of file diff --git a/test-projects/partition-to-file/rojo.json b/test-projects/partition-to-file/rojo.json deleted file mode 100644 index 0e7367f1..00000000 --- a/test-projects/partition-to-file/rojo.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "partition-to-file", - "servePort": 23456, - "partitions": { - "lib": { - "path": "foo.lua", - "target": "ReplicatedStorage.bar" - } - } -} \ No newline at end of file diff --git a/test-projects/single-sync-point/lib/main.lua b/test-projects/single-sync-point/lib/main.lua new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/single-sync-point/roblox-project.json b/test-projects/single-sync-point/roblox-project.json new file mode 100644 index 00000000..3681fb2b --- /dev/null +++ b/test-projects/single-sync-point/roblox-project.json @@ -0,0 +1,11 @@ +{ + "name": "empty", + "tree": { + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "Foo": { + "$path": "lib" + } + } + } +} \ No newline at end of file