forked from rojo-rbx/rojo
Use thiserror and anyhow for command-level error types
This commit is contained in:
29
Cargo.lock
generated
29
Cargo.lock
generated
@@ -21,6 +21,11 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -1667,11 +1672,13 @@ dependencies = [
|
||||
name = "rojo"
|
||||
version = "0.6.0-alpha.3"
|
||||
dependencies = [
|
||||
"anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fs-err 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1702,6 +1709,7 @@ dependencies = [
|
||||
"structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2052,6 +2060,24 @@ dependencies = [
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
@@ -2531,6 +2557,7 @@ dependencies = [
|
||||
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
"checksum aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
@@ -2747,6 +2774,8 @@ dependencies = [
|
||||
"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
|
||||
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db"
|
||||
"checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a"
|
||||
|
||||
@@ -63,10 +63,12 @@ harness = false
|
||||
[dependencies]
|
||||
memofs = { version = "0.1.0", path = "memofs" }
|
||||
|
||||
anyhow = "1.0.27"
|
||||
backtrace = "0.3"
|
||||
crossbeam-channel = "0.4.0"
|
||||
csv = "1.1.1"
|
||||
env_logger = "0.7.1"
|
||||
fs-err = "2.2.0"
|
||||
futures = "0.1.29"
|
||||
globset = "0.4.4"
|
||||
humantime = "1.3.0"
|
||||
@@ -90,6 +92,7 @@ serde_json = "1.0"
|
||||
snafu = "0.6.0"
|
||||
structopt = "0.3.5"
|
||||
termcolor = "1.0.5"
|
||||
thiserror = "1.0.11"
|
||||
tokio = "0.1.22"
|
||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
||||
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufWriter, Write},
|
||||
io::{BufWriter, Write},
|
||||
};
|
||||
|
||||
use memofs::Vfs;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use thiserror::Error;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::{
|
||||
cli::BuildCommand, project::ProjectError, serve_session::ServeSession, snapshot::RojoTree,
|
||||
};
|
||||
use crate::{cli::BuildCommand, serve_session::ServeSession, snapshot::RojoTree};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum OutputKind {
|
||||
@@ -31,45 +29,17 @@ fn detect_output_kind(options: &BuildCommand) -> Option<OutputKind> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub struct BuildError(Error);
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[snafu(display("Could not detect what kind of file to create"))]
|
||||
#[error("Could not detect what kind of file to build. Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.")]
|
||||
UnknownOutputKind,
|
||||
|
||||
#[snafu(display("{}", source))]
|
||||
Io { source: io::Error },
|
||||
|
||||
#[snafu(display("{}", source))]
|
||||
XmlModelEncode { source: rbx_xml::EncodeError },
|
||||
|
||||
#[snafu(display("Binary model error: {:?}", source))]
|
||||
BinaryModelEncode {
|
||||
#[snafu(source(false))]
|
||||
source: rbx_binary::EncodeError,
|
||||
},
|
||||
|
||||
#[snafu(display("{}", source))]
|
||||
Project { source: ProjectError },
|
||||
}
|
||||
|
||||
impl From<rbx_binary::EncodeError> for Error {
|
||||
fn from(source: rbx_binary::EncodeError) -> Self {
|
||||
Error::BinaryModelEncode { source }
|
||||
}
|
||||
}
|
||||
|
||||
fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||
}
|
||||
|
||||
pub fn build(options: BuildCommand) -> Result<(), BuildError> {
|
||||
Ok(build_inner(options)?)
|
||||
}
|
||||
|
||||
fn build_inner(options: BuildCommand) -> Result<(), Error> {
|
||||
pub fn build(options: BuildCommand) -> Result<(), anyhow::Error> {
|
||||
log::trace!("Constructing in-memory filesystem");
|
||||
|
||||
let vfs = Vfs::new_default();
|
||||
@@ -98,14 +68,14 @@ fn build_inner(options: BuildCommand) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), Error> {
|
||||
fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), anyhow::Error> {
|
||||
let output_kind = detect_output_kind(&options).ok_or(Error::UnknownOutputKind)?;
|
||||
log::debug!("Hoping to generate file of type {:?}", output_kind);
|
||||
|
||||
let root_id = tree.get_root_id();
|
||||
|
||||
log::trace!("Opening output file for write");
|
||||
let file = File::create(&options.output).context(Io)?;
|
||||
let file = File::create(&options.output)?;
|
||||
let mut file = BufWriter::new(file);
|
||||
|
||||
match output_kind {
|
||||
@@ -113,8 +83,7 @@ fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), Error> {
|
||||
// Model files include the root instance of the tree and all its
|
||||
// descendants.
|
||||
|
||||
rbx_xml::to_writer(&mut file, tree.inner(), &[root_id], xml_encode_config())
|
||||
.context(XmlModelEncode)?;
|
||||
rbx_xml::to_writer(&mut file, tree.inner(), &[root_id], xml_encode_config())?;
|
||||
}
|
||||
OutputKind::Rbxlx => {
|
||||
// Place files don't contain an entry for the DataModel, but our
|
||||
@@ -123,8 +92,7 @@ fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), Error> {
|
||||
let root_instance = tree.get_instance(root_id).unwrap();
|
||||
let top_level_ids = root_instance.children();
|
||||
|
||||
rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config())
|
||||
.context(XmlModelEncode)?;
|
||||
rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config())?;
|
||||
}
|
||||
OutputKind::Rbxm => {
|
||||
rbx_binary::encode(tree.inner(), &[root_id], &mut file)?;
|
||||
@@ -141,7 +109,7 @@ fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
|
||||
file.flush().context(Io)?;
|
||||
file.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,26 +1,4 @@
|
||||
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")?;
|
||||
pub fn doc() -> Result<(), anyhow::Error> {
|
||||
opener::open("https://rojo.space/docs")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use snafu::Snafu;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::cli::{InitCommand, InitKind};
|
||||
|
||||
@@ -20,32 +20,16 @@ static PLACE_PROJECT: &str =
|
||||
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)]
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[snafu(display("A project file named default.project.json already exists in this folder"))]
|
||||
#[error("A project file named default.project.json already exists in this folder")]
|
||||
AlreadyExists,
|
||||
|
||||
#[snafu(display("git init failed"))]
|
||||
#[error("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> {
|
||||
pub fn init(options: InitCommand) -> Result<(), anyhow::Error> {
|
||||
let base_path = options.absolute_path();
|
||||
fs::create_dir_all(&base_path)?;
|
||||
|
||||
@@ -65,7 +49,7 @@ fn init_inner(options: InitCommand) -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
||||
eprintln!("Creating new place project '{}'", project_params.name);
|
||||
|
||||
let project_file = project_params.render_template(PLACE_PROJECT);
|
||||
@@ -109,7 +93,7 @@ fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Err
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
||||
eprintln!("Creating new model project '{}'", project_params.name);
|
||||
|
||||
let project_file = project_params.render_template(MODEL_PROJECT);
|
||||
@@ -147,14 +131,14 @@ impl ProjectParams {
|
||||
}
|
||||
|
||||
/// Attempt to initialize a Git repository if necessary, and create .gitignore.
|
||||
fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), Error> {
|
||||
fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::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);
|
||||
return Err(Error::GitInit.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +170,7 @@ fn should_git_init(path: &Path) -> bool {
|
||||
}
|
||||
|
||||
/// Write a file if it does not exist yet, otherwise, leave it alone.
|
||||
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), Error> {
|
||||
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error> {
|
||||
let file_res = OpenOptions::new().write(true).create_new(true).open(path);
|
||||
|
||||
let mut file = match file_res {
|
||||
@@ -205,7 +189,7 @@ fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
/// Try to create a project file and fail if it already exists.
|
||||
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), Error> {
|
||||
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::Error> {
|
||||
let project_path = base_path.join("default.project.json");
|
||||
|
||||
let file_res = OpenOptions::new()
|
||||
@@ -217,7 +201,7 @@ fn try_create_project(base_path: &Path, contents: &str) -> Result<(), Error> {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
return match err.kind() {
|
||||
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists),
|
||||
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists.into()),
|
||||
_ => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,15 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use memofs::Vfs;
|
||||
use snafu::Snafu;
|
||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||
|
||||
use crate::{cli::ServeCommand, serve_session::ServeSession, web::LiveServer};
|
||||
|
||||
const DEFAULT_PORT: u16 = 34872;
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub struct ServeError(Error);
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
enum Error {}
|
||||
|
||||
pub fn serve(options: ServeCommand) -> Result<(), ServeError> {
|
||||
Ok(serve_inner(options)?)
|
||||
}
|
||||
|
||||
fn serve_inner(options: ServeCommand) -> Result<(), Error> {
|
||||
pub fn serve(options: ServeCommand) -> Result<()> {
|
||||
let vfs = Vfs::new_default();
|
||||
|
||||
let session = Arc::new(ServeSession::new(vfs, &options.absolute_project()));
|
||||
|
||||
@@ -1,34 +1,19 @@
|
||||
use memofs::Vfs;
|
||||
use reqwest::header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{auth_cookie::get_auth_cookie, cli::UploadCommand, serve_session::ServeSession};
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub struct UploadError(Error);
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[snafu(display(
|
||||
"Rojo could not find your Roblox auth cookie. Please pass one via --cookie.",
|
||||
))]
|
||||
#[error("Rojo could not find your Roblox auth cookie. Please pass one via --cookie.")]
|
||||
NeedAuthCookie,
|
||||
|
||||
#[snafu(display("XML model file encode error: {}", source))]
|
||||
XmlModel { source: rbx_xml::EncodeError },
|
||||
|
||||
#[snafu(display("HTTP error: {}", source))]
|
||||
Http { source: reqwest::Error },
|
||||
|
||||
#[snafu(display("Roblox API error: {}", body))]
|
||||
#[error("The Roblox API returned an unexpected error: {body}")]
|
||||
RobloxApi { body: String },
|
||||
}
|
||||
|
||||
pub fn upload(options: UploadCommand) -> Result<(), UploadError> {
|
||||
Ok(upload_inner(options)?)
|
||||
}
|
||||
|
||||
fn upload_inner(options: UploadCommand) -> Result<(), Error> {
|
||||
pub fn upload(options: UploadCommand) -> Result<(), anyhow::Error> {
|
||||
let cookie = options
|
||||
.cookie
|
||||
.clone()
|
||||
@@ -55,7 +40,7 @@ fn upload_inner(options: UploadCommand) -> Result<(), Error> {
|
||||
let config = rbx_xml::EncodeOptions::new()
|
||||
.property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown);
|
||||
|
||||
rbx_xml::to_writer(&mut buffer, tree.inner(), &encode_ids, config).context(XmlModel)?;
|
||||
rbx_xml::to_writer(&mut buffer, tree.inner(), &encode_ids, config)?;
|
||||
|
||||
let url = format!(
|
||||
"https://data.roblox.com/Data/Upload.ashx?assetid={}",
|
||||
@@ -72,13 +57,13 @@ fn upload_inner(options: UploadCommand) -> Result<(), Error> {
|
||||
.header(CONTENT_TYPE, "application/xml")
|
||||
.header(ACCEPT, "application/json")
|
||||
.body(buffer)
|
||||
.send()
|
||||
.context(Http)?;
|
||||
.send()?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::RobloxApi {
|
||||
body: response.text().context(Http)?,
|
||||
});
|
||||
body: response.text()?,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user