forked from rojo-rbx/rojo
Compare commits
19 Commits
memofs-v0.
...
v0.6.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd13047b58 | ||
|
|
1a83789c01 | ||
|
|
1cbe272e19 | ||
|
|
6de74b41b3 | ||
|
|
b0fc9ee507 | ||
|
|
a95ffe1d31 | ||
|
|
4119a510f5 | ||
|
|
cfa7f03815 | ||
|
|
9b4c89820d | ||
|
|
fe874720aa | ||
|
|
f7c0f33eb5 | ||
|
|
c1bf9d9dfc | ||
|
|
255bf439d3 | ||
|
|
2a31937b81 | ||
|
|
eb8964e1d1 | ||
|
|
fe0ca280a1 | ||
|
|
e8e3b7b985 | ||
|
|
c437507442 | ||
|
|
ca151b434e |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,8 +1,18 @@
|
||||
# Rojo Changelog
|
||||
|
||||
## Unreleased Changes for 0.6.x
|
||||
|
||||
## [0.6.0 Alpha 3](https://github.com/rojo-rbx/rojo/releases/tag/v0.6.0-alpha.3) (March 13, 2020)
|
||||
* Added `--watch` argument to `rojo build`. ([#284](https://github.com/rojo-rbx/rojo/pull/284))
|
||||
* Added dark theme support to plugin. ([#241](https://github.com/rojo-rbx/rojo/issues/241))
|
||||
* Added a revamped `rojo init` command, which will now create more complete projects.
|
||||
* Added the `rojo doc` command, which opens Rojo's documentation in your browser.
|
||||
* Fixed many crashes from malformed projects and filesystem edge cases in `rojo serve`.
|
||||
* Simplified filesystem access code dramatically.
|
||||
* Improved error reporting and logging across the board.
|
||||
* Log messages have a less noisy prefix.
|
||||
* Any thread panicking now causes Rojo to abort instead of existing as a zombie.
|
||||
* Errors now have a list of causes, helping make many errors more clear.
|
||||
|
||||
## [0.6.0 Alpha 2](https://github.com/rojo-rbx/rojo/releases/tag/v0.6.0-alpha.2) (March 6, 2020)
|
||||
* Fixed `rojo upload` command always uploading models.
|
||||
|
||||
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -195,7 +195,7 @@ dependencies = [
|
||||
name = "clibrojo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rojo 0.6.0-alpha.2",
|
||||
"rojo 0.6.0-alpha.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -434,18 +434,6 @@ dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -1073,6 +1061,14 @@ name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.28"
|
||||
@@ -1663,8 +1659,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rojo"
|
||||
version = "0.6.0-alpha.2"
|
||||
version = "0.6.0-alpha.3"
|
||||
dependencies = [
|
||||
"backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1680,6 +1677,7 @@ dependencies = [
|
||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memofs 0.1.0",
|
||||
"notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"opener 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rbx_binary 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1716,13 +1714,13 @@ dependencies = [
|
||||
name = "rojo-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"insta 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rbx_dom_weak 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rojo 0.6.0-alpha.2",
|
||||
"rojo 0.6.0-alpha.3",
|
||||
"rojo-insta-ext 0.1.0",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2574,7 +2572,6 @@ dependencies = [
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
|
||||
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd"
|
||||
"checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b"
|
||||
@@ -2646,6 +2643,7 @@ dependencies = [
|
||||
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||
"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405"
|
||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum opener 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13117407ca9d0caf3a0e74f97b490a7e64c0ae3aa90a8b7085544d0c37b6f3ae"
|
||||
"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52"
|
||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rojo"
|
||||
version = "0.6.0-alpha.2"
|
||||
version = "0.6.0-alpha.3"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
description = "Enables professional-grade development tools for Roblox developers"
|
||||
license = "MPL-2.0"
|
||||
@@ -15,6 +15,12 @@ exclude = [
|
||||
"/test-projects/**",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -55,6 +61,9 @@ name = "build"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
memofs = { version = "0.1.0", path = "memofs" }
|
||||
|
||||
backtrace = "0.3"
|
||||
crossbeam-channel = "0.4.0"
|
||||
csv = "1.1.1"
|
||||
env_logger = "0.7.1"
|
||||
@@ -67,6 +76,7 @@ lazy_static = "1.4.0"
|
||||
log = "0.4.8"
|
||||
maplit = "1.0.1"
|
||||
notify = "4.0.14"
|
||||
opener = "0.4.1"
|
||||
rbx_binary = "0.5.0"
|
||||
rbx_dom_weak = "1.10.1"
|
||||
rbx_reflection = "3.3.408"
|
||||
@@ -82,7 +92,6 @@ structopt = "0.3.5"
|
||||
termcolor = "1.0.5"
|
||||
tokio = "0.1.22"
|
||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
||||
memofs = { path = "memofs" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = "0.6.2"
|
||||
|
||||
11
assets/default-model-project/README.md
Normal file
11
assets/default-model-project/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# {project_name}
|
||||
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||
|
||||
## Getting Started
|
||||
To build this library or plugin, use:
|
||||
|
||||
```bash
|
||||
rojo build -o "{project_name}.rbxmx"
|
||||
```
|
||||
|
||||
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||
6
assets/default-model-project/default.project.json
Normal file
6
assets/default-model-project/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "{project_name}",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
}
|
||||
3
assets/default-model-project/gitignore.txt
Normal file
3
assets/default-model-project/gitignore.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# Roblox Studio lock files
|
||||
/*.rbxlx.lock
|
||||
/*.rbxl.lock
|
||||
5
assets/default-model-project/src-init.lua
Normal file
5
assets/default-model-project/src-init.lua
Normal file
@@ -0,0 +1,5 @@
|
||||
return {
|
||||
hello = function()
|
||||
print("Hello world, from {project_name}!")
|
||||
end,
|
||||
}
|
||||
17
assets/default-place-project/README.md
Normal file
17
assets/default-place-project/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# {project_name}
|
||||
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||
|
||||
## Getting Started
|
||||
To build the place from scratch, use:
|
||||
|
||||
```bash
|
||||
rojo build -o "{project_name}.rbxlx"
|
||||
```
|
||||
|
||||
Next, open `{project_name}.rbxlx` in Roblox Studio and start the Rojo server:
|
||||
|
||||
```bash
|
||||
rojo serve
|
||||
```
|
||||
|
||||
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||
@@ -1,39 +1,36 @@
|
||||
{
|
||||
"name": "[placeholder]",
|
||||
"name": "{project_name}",
|
||||
"tree": {
|
||||
"$className": "DataModel",
|
||||
"HttpService": {
|
||||
"$className": "HttpService",
|
||||
"$properties": {
|
||||
"HttpEnabled": true
|
||||
}
|
||||
},
|
||||
"Lighting": {
|
||||
"$className": "Lighting",
|
||||
"$properties": {
|
||||
"Ambient": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Brightness": 2,
|
||||
"GlobalShadows": true,
|
||||
"Outlines": false,
|
||||
"Technology": "Voxel"
|
||||
}
|
||||
},
|
||||
|
||||
"ReplicatedStorage": {
|
||||
"$className": "ReplicatedStorage",
|
||||
"Source": {
|
||||
"$path": "src"
|
||||
|
||||
"Common": {
|
||||
"$path": "src/common"
|
||||
}
|
||||
},
|
||||
"SoundService": {
|
||||
"$className": "SoundService",
|
||||
"$properties": {
|
||||
"RespectFilteringEnabled": true
|
||||
|
||||
"ServerScriptService": {
|
||||
"$className": "ServerScriptService",
|
||||
|
||||
"Server": {
|
||||
"$path": "src/server"
|
||||
}
|
||||
},
|
||||
|
||||
"StarterPlayer": {
|
||||
"$className": "StarterPlayer",
|
||||
|
||||
"StarterPlayerScripts": {
|
||||
"$className": "StarterPlayerScripts",
|
||||
|
||||
"Client": {
|
||||
"$path": "src/client"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"Workspace": {
|
||||
"$className": "Workspace",
|
||||
"$properties": {
|
||||
@@ -61,6 +58,32 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Lighting": {
|
||||
"$className": "Lighting",
|
||||
"$properties": {
|
||||
"Ambient": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Brightness": 2,
|
||||
"GlobalShadows": true,
|
||||
"Outlines": false,
|
||||
"Technology": "Voxel"
|
||||
}
|
||||
},
|
||||
"SoundService": {
|
||||
"$className": "SoundService",
|
||||
"$properties": {
|
||||
"RespectFilteringEnabled": true
|
||||
}
|
||||
},
|
||||
"HttpService": {
|
||||
"$className": "HttpService",
|
||||
"$properties": {
|
||||
"HttpEnabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
assets/default-place-project/gitignore.txt
Normal file
6
assets/default-place-project/gitignore.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# Project place file
|
||||
/{project_name}.rbxlx
|
||||
|
||||
# Roblox Studio lock files
|
||||
/*.rbxlx.lock
|
||||
/*.rbxl.lock
|
||||
6
memofs/CHANGELOG.md
Normal file
6
memofs/CHANGELOG.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# memofs Changelog
|
||||
|
||||
## Unreleased Changes
|
||||
|
||||
## 0.1.0 (2020-03-10)
|
||||
* Initial release
|
||||
@@ -17,7 +17,8 @@ function Panel:render()
|
||||
return Theme.with(function(theme)
|
||||
return e("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
BackgroundColor3 = theme.Background1,
|
||||
BorderSizePixel = 1,
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
@@ -26,8 +27,7 @@ function Panel:render()
|
||||
|
||||
Body = e("Frame", {
|
||||
Size = UDim2.new(0, 360, 1, -32),
|
||||
BackgroundColor3 = theme.Background1,
|
||||
BorderSizePixel = 0,
|
||||
BackgroundTransparency = 1,
|
||||
}, self.props[Roact.Children]),
|
||||
|
||||
Footer = e(RojoFooter),
|
||||
|
||||
@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
||||
return strict("Config", {
|
||||
isDevBuild = isDevBuild,
|
||||
codename = "Epiphany",
|
||||
version = {0, 6, 0, "-alpha.2"},
|
||||
version = {0, 6, 0, "-alpha.3"},
|
||||
expectedServerVersionString = "0.6.0 or newer",
|
||||
protocolVersion = 3,
|
||||
defaultHost = "localhost",
|
||||
|
||||
@@ -11,7 +11,7 @@ default = []
|
||||
unstable_glob_ignore_paths = []
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.6.2"
|
||||
env_logger = "0.7.1"
|
||||
insta = { version = "0.13.1", features = ["redactions"] }
|
||||
log = "0.4.8"
|
||||
paste = "0.1.5"
|
||||
|
||||
124
src/bin.rs
124
src/bin.rs
@@ -1,65 +1,93 @@
|
||||
use std::{error::Error, panic, process};
|
||||
use std::{env, error::Error, panic, process};
|
||||
|
||||
use log::error;
|
||||
use backtrace::Backtrace;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use librojo::cli::{self, Options, Subcommand};
|
||||
|
||||
fn main() {
|
||||
let options = Options::from_args();
|
||||
|
||||
{
|
||||
let log_filter = match options.verbosity {
|
||||
0 => "warn",
|
||||
1 => "warn,librojo=info",
|
||||
2 => "warn,librojo=trace",
|
||||
_ => "trace",
|
||||
};
|
||||
|
||||
let log_env = env_logger::Env::default().default_filter_or(log_filter);
|
||||
|
||||
env_logger::Builder::from_env(log_env)
|
||||
.format_timestamp(None)
|
||||
.init();
|
||||
}
|
||||
|
||||
let panic_result = panic::catch_unwind(|| {
|
||||
if let Err(err) = run(options.subcommand) {
|
||||
log::error!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(error) = panic_result {
|
||||
let message = match error.downcast_ref::<&str>() {
|
||||
Some(message) => message.to_string(),
|
||||
None => match error.downcast_ref::<String>() {
|
||||
Some(message) => message.clone(),
|
||||
None => "<no message>".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
show_crash_message(&message);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(subcommand: Subcommand) -> Result<(), Box<dyn Error>> {
|
||||
match subcommand {
|
||||
Subcommand::Init(init_options) => cli::init(init_options)?,
|
||||
Subcommand::Serve(serve_options) => cli::serve(serve_options)?,
|
||||
Subcommand::Build(build_options) => cli::build(build_options)?,
|
||||
Subcommand::Upload(upload_options) => cli::upload(upload_options)?,
|
||||
Subcommand::Doc => cli::doc()?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_crash_message(message: &str) {
|
||||
error!("Rojo crashed!");
|
||||
error!("This is a bug in Rojo.");
|
||||
error!("");
|
||||
error!("Please consider filing a bug: https://github.com/rojo-rbx/rojo/issues");
|
||||
error!("");
|
||||
error!("Details: {}", message);
|
||||
fn main() {
|
||||
panic::set_hook(Box::new(|panic_info| {
|
||||
// PanicInfo's payload is usually a &'static str or String.
|
||||
// See: https://doc.rust-lang.org/beta/std/panic/struct.PanicInfo.html#method.payload
|
||||
let message = match panic_info.payload().downcast_ref::<&str>() {
|
||||
Some(message) => message.to_string(),
|
||||
None => match panic_info.payload().downcast_ref::<String>() {
|
||||
Some(message) => message.clone(),
|
||||
None => "<no message>".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
log::error!("Rojo crashed!");
|
||||
log::error!("This is probably a Rojo bug.");
|
||||
log::error!("");
|
||||
log::error!(
|
||||
"Please consider filing an issue: {}/issues",
|
||||
env!("CARGO_PKG_REPOSITORY")
|
||||
);
|
||||
log::error!("");
|
||||
log::error!("Details: {}", message);
|
||||
|
||||
if let Some(location) = panic_info.location() {
|
||||
log::error!("in file {} on line {}", location.file(), location.line());
|
||||
}
|
||||
|
||||
// When using the backtrace crate, we need to check the RUST_BACKTRACE
|
||||
// environment variable ourselves. Once we switch to the (currently
|
||||
// unstable) std::backtrace module, we won't need to do this anymore.
|
||||
let should_backtrace = env::var("RUST_BACKTRACE")
|
||||
.map(|var| var == "1")
|
||||
.unwrap_or(false);
|
||||
|
||||
if should_backtrace {
|
||||
eprintln!("{:?}", Backtrace::new());
|
||||
} else {
|
||||
eprintln!(
|
||||
"note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace."
|
||||
);
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}));
|
||||
|
||||
let options = Options::from_args();
|
||||
|
||||
let log_filter = match options.verbosity {
|
||||
0 => "warn",
|
||||
1 => "warn,librojo=info",
|
||||
2 => "warn,librojo=trace",
|
||||
_ => "trace",
|
||||
};
|
||||
|
||||
let log_env = env_logger::Env::default().default_filter_or(log_filter);
|
||||
|
||||
env_logger::Builder::from_env(log_env)
|
||||
.format_module_path(false)
|
||||
.format_timestamp(None)
|
||||
// Indent following lines equal to the log level label, like `[ERROR] `
|
||||
.format_indent(Some(8))
|
||||
.init();
|
||||
|
||||
if let Err(err) = run(options.subcommand) {
|
||||
log::error!("{}", err);
|
||||
|
||||
let mut current_err: &dyn Error = &*err;
|
||||
while let Some(source) = current_err.source() {
|
||||
log::error!(" caused by {}", source);
|
||||
current_err = &*source;
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use memofs::{IoResultExt, Vfs, VfsEvent};
|
||||
use rbx_dom_weak::{RbxId, RbxValue};
|
||||
|
||||
use crate::{
|
||||
error::ErrorDisplay,
|
||||
message_queue::MessageQueue,
|
||||
snapshot::{
|
||||
apply_patch_set, compute_patch_set, AppliedPatchSet, InstigatingSource, PatchSet, RojoTree,
|
||||
@@ -294,9 +295,15 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
|
||||
// starting at that path and use it as the source for
|
||||
// our patch.
|
||||
|
||||
let snapshot = snapshot_from_vfs(&metadata.context, &vfs, &path)
|
||||
.expect("snapshot failed")
|
||||
.expect("snapshot did not return an instance");
|
||||
let snapshot = match snapshot_from_vfs(&metadata.context, &vfs, &path) {
|
||||
Ok(maybe_snapshot) => {
|
||||
maybe_snapshot.expect("snapshot did not return an instance")
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Snapshot error: {}", ErrorDisplay(err));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let patch_set = compute_patch_set(&snapshot, &tree, id);
|
||||
apply_patch_set(tree, patch_set)
|
||||
@@ -320,15 +327,21 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
|
||||
// there might be information associated with our instance from
|
||||
// the project file, we snapshot the entire project node again.
|
||||
|
||||
let snapshot = snapshot_project_node(
|
||||
let snapshot_result = snapshot_project_node(
|
||||
&metadata.context,
|
||||
&project_path,
|
||||
instance_name,
|
||||
project_node,
|
||||
&vfs,
|
||||
)
|
||||
.expect("snapshot failed")
|
||||
.expect("snapshot did not return an instance");
|
||||
);
|
||||
|
||||
let snapshot = match snapshot_result {
|
||||
Ok(maybe_snapshot) => maybe_snapshot.expect("snapshot did not return an instance"),
|
||||
Err(err) => {
|
||||
log::error!("Snapshot error: {}", ErrorDisplay(err));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let patch_set = compute_patch_set(&snapshot, &tree, id);
|
||||
apply_patch_set(tree, patch_set)
|
||||
|
||||
26
src/cli/doc.rs
Normal file
26
src/cli/doc.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use opener::{open, OpenError};
|
||||
use snafu::Snafu;
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub struct DocError(Error);
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
enum Error {
|
||||
Open { source: OpenError },
|
||||
}
|
||||
|
||||
impl From<OpenError> for Error {
|
||||
fn from(source: OpenError) -> Self {
|
||||
Error::Open { source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doc() -> Result<(), DocError> {
|
||||
doc_inner()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn doc_inner() -> Result<(), Error> {
|
||||
open("https://rojo.space/docs")?;
|
||||
Ok(())
|
||||
}
|
||||
220
src/cli/init.rs
220
src/cli/init.rs
@@ -1,17 +1,229 @@
|
||||
use std::{
|
||||
fs::{self, OpenOptions},
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use snafu::Snafu;
|
||||
|
||||
use crate::cli::InitCommand;
|
||||
use crate::cli::{InitCommand, InitKind};
|
||||
|
||||
static MODEL_PROJECT: &str =
|
||||
include_str!("../../assets/default-model-project/default.project.json");
|
||||
static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md");
|
||||
static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.lua");
|
||||
static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt");
|
||||
|
||||
static PLACE_PROJECT: &str =
|
||||
include_str!("../../assets/default-place-project/default.project.json");
|
||||
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
||||
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub struct InitError(Error);
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
enum Error {}
|
||||
enum Error {
|
||||
#[snafu(display("A project file named default.project.json already exists in this folder"))]
|
||||
AlreadyExists,
|
||||
|
||||
#[snafu(display("git init failed"))]
|
||||
GitInit,
|
||||
|
||||
#[snafu(display("I/O error"))]
|
||||
Io { source: io::Error },
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(source: io::Error) -> Self {
|
||||
Self::Io { source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(options: InitCommand) -> Result<(), InitError> {
|
||||
Ok(init_inner(options)?)
|
||||
}
|
||||
|
||||
fn init_inner(_options: InitCommand) -> Result<(), Error> {
|
||||
unimplemented!("init command");
|
||||
fn init_inner(options: InitCommand) -> Result<(), Error> {
|
||||
let base_path = options.absolute_path();
|
||||
fs::create_dir_all(&base_path)?;
|
||||
|
||||
let canonical = fs::canonicalize(&base_path)?;
|
||||
let project_name = canonical
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("new-project");
|
||||
|
||||
let project_params = ProjectParams {
|
||||
name: project_name.to_owned(),
|
||||
};
|
||||
|
||||
match options.kind {
|
||||
InitKind::Place => init_place(&base_path, project_params),
|
||||
InitKind::Model => init_model(&base_path, project_params),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||
eprintln!("Creating new place project '{}'", project_params.name);
|
||||
|
||||
let project_file = project_params.render_template(PLACE_PROJECT);
|
||||
try_create_project(base_path, &project_file)?;
|
||||
|
||||
let readme = project_params.render_template(PLACE_README);
|
||||
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
||||
|
||||
let src = base_path.join("src");
|
||||
fs::create_dir_all(&src)?;
|
||||
|
||||
let src_shared = src.join("shared");
|
||||
fs::create_dir_all(src.join(&src_shared))?;
|
||||
|
||||
let src_server = src.join("server");
|
||||
fs::create_dir_all(src.join(&src_server))?;
|
||||
|
||||
let src_client = src.join("client");
|
||||
fs::create_dir_all(src.join(&src_client))?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_shared.join("Hello.lua"),
|
||||
"return function()\n\tprint(\"Hello, world!\")\nend",
|
||||
)?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_server.join("init.server.lua"),
|
||||
"print(\"Hello world, from server!\")",
|
||||
)?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_client.join("init.client.lua"),
|
||||
"print(\"Hello world, from client!\")",
|
||||
)?;
|
||||
|
||||
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
||||
try_git_init(base_path, &git_ignore)?;
|
||||
|
||||
eprintln!("Created project successfully.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||
eprintln!("Creating new model project '{}'", project_params.name);
|
||||
|
||||
let project_file = project_params.render_template(MODEL_PROJECT);
|
||||
try_create_project(base_path, &project_file)?;
|
||||
|
||||
let readme = project_params.render_template(MODEL_README);
|
||||
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
||||
|
||||
let src = base_path.join("src");
|
||||
fs::create_dir_all(&src)?;
|
||||
|
||||
let init = project_params.render_template(MODEL_INIT);
|
||||
write_if_not_exists(&src.join("init.lua"), &init)?;
|
||||
|
||||
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
||||
try_git_init(base_path, &git_ignore)?;
|
||||
|
||||
eprintln!("Created project successfully.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Contains parameters used in templates to create a project.
|
||||
struct ProjectParams {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ProjectParams {
|
||||
/// Render a template by replacing variables with project parameters.
|
||||
fn render_template(&self, template: &str) -> String {
|
||||
template
|
||||
.replace("{project_name}", &self.name)
|
||||
.replace("{rojo_version}", env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to initialize a Git repository if necessary, and create .gitignore.
|
||||
fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), Error> {
|
||||
if should_git_init(path) {
|
||||
log::debug!("Initializing Git repository...");
|
||||
|
||||
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(Error::GitInit);
|
||||
}
|
||||
}
|
||||
|
||||
write_if_not_exists(&path.join(".gitignore"), git_ignore)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tells whether we should initialize a Git repository inside the given path.
|
||||
///
|
||||
/// Will return false if the user doesn't have Git installed or if the path is
|
||||
/// already inside a Git repository.
|
||||
fn should_git_init(path: &Path) -> bool {
|
||||
let result = Command::new("git")
|
||||
.args(&["rev-parse", "--is-inside-work-tree"])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.current_dir(path)
|
||||
.status();
|
||||
|
||||
match result {
|
||||
// If the command ran, but returned a non-zero exit code, we are not in
|
||||
// a Git repo and we should initialize one.
|
||||
Ok(status) => !status.success(),
|
||||
|
||||
// If the command failed to run, we probably don't have Git installed.
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a file if it does not exist yet, otherwise, leave it alone.
|
||||
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), Error> {
|
||||
let file_res = OpenOptions::new().write(true).create_new(true).open(path);
|
||||
|
||||
let mut file = match file_res {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
return match err.kind() {
|
||||
io::ErrorKind::AlreadyExists => return Ok(()),
|
||||
_ => Err(err.into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
file.write_all(contents.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Try to create a project file and fail if it already exists.
|
||||
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), Error> {
|
||||
let project_path = base_path.join("default.project.json");
|
||||
|
||||
let file_res = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(project_path);
|
||||
|
||||
let mut file = match file_res {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
return match err.kind() {
|
||||
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists),
|
||||
_ => Err(err.into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
file.write_all(contents.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Defines Rojo's CLI through structopt types.
|
||||
|
||||
mod build;
|
||||
mod doc;
|
||||
mod init;
|
||||
mod serve;
|
||||
mod upload;
|
||||
@@ -17,6 +18,7 @@ use std::{
|
||||
use structopt::StructOpt;
|
||||
|
||||
pub use self::build::*;
|
||||
pub use self::doc::*;
|
||||
pub use self::init::*;
|
||||
pub use self::serve::*;
|
||||
pub use self::upload::*;
|
||||
@@ -48,6 +50,9 @@ pub enum Subcommand {
|
||||
|
||||
/// Generates a place or model file out of the project and uploads it to Roblox.
|
||||
Upload(UploadCommand),
|
||||
|
||||
/// Open Rojo's documentation in your browser.
|
||||
Doc,
|
||||
}
|
||||
|
||||
/// Initializes a new Rojo project.
|
||||
|
||||
18
src/error.rs
Normal file
18
src/error.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
/// Wrapper type to print errors with source-chasing.
|
||||
pub struct ErrorDisplay<E>(pub E);
|
||||
|
||||
impl<E: Error> fmt::Display for ErrorDisplay<E> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(formatter, "{}", self.0)?;
|
||||
|
||||
let mut current_err: &dyn Error = &self.0;
|
||||
while let Some(source) = current_err.source() {
|
||||
writeln!(formatter, " caused by {}", source)?;
|
||||
current_err = &*source;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ mod tree_view;
|
||||
mod auth_cookie;
|
||||
mod change_processor;
|
||||
mod common_setup;
|
||||
mod error;
|
||||
mod glob;
|
||||
mod message_queue;
|
||||
mod multimap;
|
||||
|
||||
@@ -46,8 +46,8 @@ impl SnapshotMiddleware for SnapshotCsv {
|
||||
.relevant_paths(vec![path.to_path_buf(), meta_path.clone()]),
|
||||
);
|
||||
|
||||
if let Some(meta_contents) = vfs.read(meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents);
|
||||
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
|
||||
metadata.apply_all(&mut snapshot);
|
||||
}
|
||||
|
||||
@@ -131,52 +131,58 @@ fn convert_localization_csv(contents: &[u8]) -> String {
|
||||
serde_json::to_string(&entries).expect("Could not encode JSON for localization table")
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "FIXME"))]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::vfs::{NoopFetcher, VfsDebug, VfsSnapshot};
|
||||
use insta::assert_yaml_snapshot;
|
||||
use memofs::{InMemoryFs, VfsSnapshot};
|
||||
|
||||
#[test]
|
||||
fn csv_from_vfs() {
|
||||
let mut vfs = Vfs::new(NoopFetcher);
|
||||
let file = VfsSnapshot::file(
|
||||
r#"
|
||||
let mut imfs = InMemoryFs::new();
|
||||
imfs.load_snapshot(
|
||||
"/foo.csv",
|
||||
VfsSnapshot::file(
|
||||
r#"
|
||||
Key,Source,Context,Example,es
|
||||
Ack,Ack!,,An exclamation of despair,¡Ay!"#,
|
||||
);
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
vfs.debug_load_snapshot("/foo.csv", file);
|
||||
let mut vfs = Vfs::new(imfs);
|
||||
|
||||
let entry = vfs.get("/foo.csv").unwrap();
|
||||
let instance_snapshot =
|
||||
SnapshotCsv::from_vfs(&InstanceContext::default(), &mut vfs, &entry)
|
||||
SnapshotCsv::from_vfs(&InstanceContext::default(), &mut vfs, Path::new("/foo.csv"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_yaml_snapshot!(instance_snapshot);
|
||||
insta::assert_yaml_snapshot!(instance_snapshot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csv_with_meta() {
|
||||
let mut vfs = Vfs::new(NoopFetcher);
|
||||
let file = VfsSnapshot::file(
|
||||
r#"
|
||||
let mut imfs = InMemoryFs::new();
|
||||
imfs.load_snapshot(
|
||||
"/foo.csv",
|
||||
VfsSnapshot::file(
|
||||
r#"
|
||||
Key,Source,Context,Example,es
|
||||
Ack,Ack!,,An exclamation of despair,¡Ay!"#,
|
||||
);
|
||||
let meta = VfsSnapshot::file(r#"{ "ignoreUnknownInstances": true }"#);
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
imfs.load_snapshot(
|
||||
"/foo.meta.json",
|
||||
VfsSnapshot::file(r#"{ "ignoreUnknownInstances": true }"#),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
vfs.debug_load_snapshot("/foo.csv", file);
|
||||
vfs.debug_load_snapshot("/foo.meta.json", meta);
|
||||
let mut vfs = Vfs::new(imfs);
|
||||
|
||||
let entry = vfs.get("/foo.csv").unwrap();
|
||||
let instance_snapshot =
|
||||
SnapshotCsv::from_vfs(&InstanceContext::default(), &mut vfs, &entry)
|
||||
SnapshotCsv::from_vfs(&InstanceContext::default(), &mut vfs, Path::new("/foo.csv"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_yaml_snapshot!(instance_snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@ impl SnapshotMiddleware for SnapshotDir {
|
||||
.context(context),
|
||||
);
|
||||
|
||||
if let Some(meta_contents) = vfs.read(meta_path).with_not_found()? {
|
||||
let mut metadata = DirectoryMetadata::from_slice(&meta_contents);
|
||||
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
||||
let mut metadata = DirectoryMetadata::from_slice(&meta_contents, &meta_path)?;
|
||||
metadata.apply_all(&mut snapshot);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::{error::Error, fmt, io, path::PathBuf};
|
||||
|
||||
use snafu::Snafu;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SnapshotError {
|
||||
detail: SnapshotErrorDetail,
|
||||
@@ -8,49 +10,63 @@ pub struct SnapshotError {
|
||||
|
||||
impl SnapshotError {
|
||||
pub fn new(detail: SnapshotErrorDetail, path: Option<impl Into<PathBuf>>) -> Self {
|
||||
SnapshotError {
|
||||
Self {
|
||||
detail,
|
||||
path: path.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wrap(inner: impl Into<SnapshotErrorDetail>, path: impl Into<PathBuf>) -> Self {
|
||||
SnapshotError {
|
||||
detail: inner.into(),
|
||||
pub(crate) fn wrap(source: impl Into<SnapshotErrorDetail>, path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
detail: source.into(),
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_did_not_exist(path: impl Into<PathBuf>) -> SnapshotError {
|
||||
SnapshotError {
|
||||
pub(crate) fn file_did_not_exist(path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::FileDidNotExist,
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_name_bad_unicode(path: impl Into<PathBuf>) -> SnapshotError {
|
||||
SnapshotError {
|
||||
pub(crate) fn file_name_bad_unicode(path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::FileNameBadUnicode,
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_contents_bad_unicode(
|
||||
inner: std::str::Utf8Error,
|
||||
source: std::str::Utf8Error,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> SnapshotError {
|
||||
SnapshotError {
|
||||
detail: SnapshotErrorDetail::FileContentsBadUnicode { inner },
|
||||
) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::FileContentsBadUnicode { source },
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_project(
|
||||
inner: serde_json::Error,
|
||||
pub(crate) fn malformed_project(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::MalformedProject { source },
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_model_json(
|
||||
source: serde_json::Error,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> SnapshotError {
|
||||
SnapshotError {
|
||||
detail: SnapshotErrorDetail::MalformedProject { inner },
|
||||
) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::MalformedModelJson { source },
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_meta_json(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
detail: SnapshotErrorDetail::MalformedMetaJson { source },
|
||||
path: Some(path.into()),
|
||||
}
|
||||
}
|
||||
@@ -83,55 +99,41 @@ impl From<rlua::Error> for SnapshotError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum SnapshotErrorDetail {
|
||||
IoError { inner: io::Error },
|
||||
Lua { inner: rlua::Error },
|
||||
#[snafu(display("I/O error"))]
|
||||
IoError { source: io::Error },
|
||||
|
||||
#[snafu(display("Lua error"))]
|
||||
Lua { source: rlua::Error },
|
||||
|
||||
#[snafu(display("file did not exist"))]
|
||||
FileDidNotExist,
|
||||
|
||||
#[snafu(display("file name had malformed Unicode"))]
|
||||
FileNameBadUnicode,
|
||||
FileContentsBadUnicode { inner: std::str::Utf8Error },
|
||||
MalformedProject { inner: serde_json::Error },
|
||||
|
||||
#[snafu(display("file had malformed Unicode contents"))]
|
||||
FileContentsBadUnicode { source: std::str::Utf8Error },
|
||||
|
||||
#[snafu(display("malformed project file"))]
|
||||
MalformedProject { source: serde_json::Error },
|
||||
|
||||
#[snafu(display("malformed .model.json file"))]
|
||||
MalformedModelJson { source: serde_json::Error },
|
||||
|
||||
#[snafu(display("malformed .meta.json file"))]
|
||||
MalformedMetaJson { source: serde_json::Error },
|
||||
}
|
||||
|
||||
impl From<io::Error> for SnapshotErrorDetail {
|
||||
fn from(inner: io::Error) -> Self {
|
||||
SnapshotErrorDetail::IoError { inner }
|
||||
fn from(source: io::Error) -> Self {
|
||||
SnapshotErrorDetail::IoError { source }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rlua::Error> for SnapshotErrorDetail {
|
||||
fn from(inner: rlua::Error) -> Self {
|
||||
SnapshotErrorDetail::Lua { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotErrorDetail {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use self::SnapshotErrorDetail::*;
|
||||
|
||||
match self {
|
||||
IoError { inner } => Some(inner),
|
||||
Lua { inner } => Some(inner),
|
||||
FileContentsBadUnicode { inner } => Some(inner),
|
||||
MalformedProject { inner } => Some(inner),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SnapshotErrorDetail {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::SnapshotErrorDetail::*;
|
||||
|
||||
match self {
|
||||
IoError { inner } => write!(formatter, "I/O error: {}", inner),
|
||||
Lua { inner } => write!(formatter, "{}", inner),
|
||||
FileDidNotExist => write!(formatter, "file did not exist"),
|
||||
FileNameBadUnicode => write!(formatter, "file name had malformed Unicode"),
|
||||
FileContentsBadUnicode { inner } => {
|
||||
write!(formatter, "file had malformed unicode: {}", inner)
|
||||
}
|
||||
MalformedProject { inner } => write!(formatter, "{}", inner),
|
||||
}
|
||||
fn from(source: rlua::Error) -> Self {
|
||||
SnapshotErrorDetail::Lua { source }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use serde::Deserialize;
|
||||
use crate::snapshot::{InstanceContext, InstanceSnapshot};
|
||||
|
||||
use super::{
|
||||
error::SnapshotError,
|
||||
middleware::{SnapshotInstanceResult, SnapshotMiddleware},
|
||||
util::match_file_name,
|
||||
};
|
||||
@@ -27,8 +28,9 @@ impl SnapshotMiddleware for SnapshotJsonModel {
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let instance: JsonModel =
|
||||
serde_json::from_slice(&vfs.read(path)?).expect("TODO: Handle serde_json errors");
|
||||
let contents = vfs.read(path)?;
|
||||
let instance: JsonModel = serde_json::from_slice(&contents)
|
||||
.map_err(|source| SnapshotError::malformed_model_json(source, path))?;
|
||||
|
||||
if let Some(json_name) = &instance.name {
|
||||
if json_name != instance_name {
|
||||
|
||||
@@ -87,8 +87,8 @@ fn snapshot_lua_file(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snaps
|
||||
.context(context),
|
||||
);
|
||||
|
||||
if let Some(meta_contents) = vfs.read(meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents);
|
||||
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
|
||||
metadata.apply_all(&mut snapshot);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use std::{borrow::Cow, collections::HashMap, path::Path};
|
||||
|
||||
use rbx_dom_weak::UnresolvedRbxValue;
|
||||
use rbx_reflection::try_resolve_value;
|
||||
@@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::snapshot::InstanceSnapshot;
|
||||
|
||||
use super::error::SnapshotError;
|
||||
|
||||
/// Represents metadata in a sibling file with the same basename.
|
||||
///
|
||||
/// As an example, hello.meta.json next to hello.lua would allow assigning
|
||||
@@ -21,10 +23,9 @@ pub struct AdjacentMetadata {
|
||||
}
|
||||
|
||||
impl AdjacentMetadata {
|
||||
pub fn from_slice(slice: &[u8]) -> Self {
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
|
||||
serde_json::from_slice(slice)
|
||||
// TODO: Turn into error type
|
||||
.expect(".meta.json file was malformed")
|
||||
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
|
||||
}
|
||||
|
||||
pub fn apply_ignore_unknown_instances(&mut self, snapshot: &mut InstanceSnapshot) {
|
||||
@@ -74,10 +75,9 @@ pub struct DirectoryMetadata {
|
||||
}
|
||||
|
||||
impl DirectoryMetadata {
|
||||
pub fn from_slice(slice: &[u8]) -> Self {
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
|
||||
serde_json::from_slice(slice)
|
||||
// TODO: Turn into error type
|
||||
.expect("init.meta.json file was malformed")
|
||||
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
|
||||
}
|
||||
|
||||
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) {
|
||||
|
||||
@@ -52,8 +52,8 @@ impl SnapshotMiddleware for SnapshotTxt {
|
||||
.context(context),
|
||||
);
|
||||
|
||||
if let Some(meta_contents) = vfs.read(meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents);
|
||||
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
||||
let mut metadata = AdjacentMetadata::from_slice(&meta_contents, &meta_path)?;
|
||||
metadata.apply_all(&mut snapshot);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user