diff --git a/Cargo.lock b/Cargo.lock index e2939241..bb9bdff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1615,7 +1615,6 @@ dependencies = [ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 417f0c30..70599768 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ harness = false crossbeam-channel = "0.3.9" csv = "1.0" env_logger = "0.6" -failure = "0.1.3" futures = "0.1" humantime = "1.3.0" hyper = "0.12" diff --git a/src/bin.rs b/src/bin.rs index aba23d30..bf371721 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -1,6 +1,5 @@ -use std::{panic, process}; +use std::{error::Error, panic, process}; -use failure::Error; use log::error; use structopt::StructOpt; @@ -45,7 +44,7 @@ fn main() { } } -fn run(subcommand: Subcommand) -> Result<(), Error> { +fn run(subcommand: Subcommand) -> Result<(), Box> { match subcommand { Subcommand::Init(init_options) => cli::init(init_options)?, Subcommand::Serve(serve_options) => cli::serve(serve_options)?, diff --git a/src/cli/build.rs b/src/cli/build.rs index 1e3a03a5..d845e970 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -3,13 +3,13 @@ use std::{ io::{self, BufWriter, Write}, }; -use failure::Fail; +use snafu::{ResultExt, Snafu}; use crate::{ cli::BuildCommand, common_setup, project::ProjectError, - vfs::{FsError, RealFetcher, Vfs, WatchMode}, + vfs::{RealFetcher, Vfs, WatchMode}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -32,41 +32,46 @@ fn detect_output_kind(options: &BuildCommand) -> Option { } } -#[derive(Debug, Fail)] -pub enum BuildError { - #[fail(display = "Could not detect what kind of file to create")] +#[derive(Debug, Snafu)] +pub struct BuildError(Error); + +#[derive(Debug, Snafu)] +enum Error { + #[snafu(display("Could not detect what kind of file to create"))] UnknownOutputKind, - #[fail(display = "IO error: {}", _0)] - IoError(#[fail(cause)] io::Error), + #[snafu(display("{}", source))] + Io { source: io::Error }, - #[fail(display = "XML model error: {}", _0)] - XmlModelEncodeError(#[fail(cause)] rbx_xml::EncodeError), + #[snafu(display("{}", source))] + XmlModelEncode { source: rbx_xml::EncodeError }, - #[fail(display = "Binary model error: {:?}", _0)] - BinaryModelEncodeError(rbx_binary::EncodeError), + #[snafu(display("Binary model error: {:?}", source))] + BinaryModelEncode { + #[snafu(source(false))] + source: rbx_binary::EncodeError, + }, - #[fail(display = "{}", _0)] - ProjectError(#[fail(cause)] ProjectError), - - #[fail(display = "{}", _0)] - FsError(#[fail(cause)] FsError), + #[snafu(display("{}", source))] + Project { source: ProjectError }, } -impl_from!(BuildError { - io::Error => IoError, - rbx_xml::EncodeError => XmlModelEncodeError, - rbx_binary::EncodeError => BinaryModelEncodeError, - ProjectError => ProjectError, - FsError => FsError, -}); +impl From 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> { - let output_kind = detect_output_kind(&options).ok_or(BuildError::UnknownOutputKind)?; + Ok(build_inner(options)?) +} + +fn build_inner(options: BuildCommand) -> Result<(), Error> { + let output_kind = detect_output_kind(&options).ok_or(Error::UnknownOutputKind)?; log::debug!("Hoping to generate file of type {:?}", output_kind); @@ -77,14 +82,17 @@ pub fn build(options: BuildCommand) -> Result<(), BuildError> { let root_id = tree.get_root_id(); log::trace!("Opening output file for write"); - let mut file = BufWriter::new(File::create(&options.output)?); + + let file = File::create(&options.output).context(Io)?; + let mut file = BufWriter::new(file); match output_kind { OutputKind::Rbxmx => { // 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())?; + rbx_xml::to_writer(&mut file, tree.inner(), &[root_id], xml_encode_config()) + .context(XmlModelEncode)?; } OutputKind::Rbxlx => { // Place files don't contain an entry for the DataModel, but our @@ -93,7 +101,8 @@ pub fn build(options: BuildCommand) -> Result<(), BuildError> { 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())?; + rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config()) + .context(XmlModelEncode)?; } OutputKind::Rbxm => { rbx_binary::encode(tree.inner(), &[root_id], &mut file)?; @@ -110,7 +119,7 @@ pub fn build(options: BuildCommand) -> Result<(), BuildError> { } } - file.flush()?; + file.flush().context(Io)?; log::trace!("Done!"); diff --git a/src/cli/init.rs b/src/cli/init.rs index 2ea6b5e3..c27360b1 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -1,17 +1,17 @@ -use failure::Fail; +use snafu::Snafu; -use crate::{cli::InitCommand, project::ProjectError}; +use crate::cli::InitCommand; -#[derive(Debug, Fail)] -pub enum InitError { - #[fail(display = "Project init error: {}", _0)] - ProjectError(#[fail(cause)] ProjectError), +#[derive(Debug, Snafu)] +pub struct InitError(Error); + +#[derive(Debug, Snafu)] +enum Error {} + +pub fn init(options: InitCommand) -> Result<(), InitError> { + Ok(init_inner(options)?) } -impl_from!(InitError { - ProjectError => ProjectError, -}); - -pub fn init(_options: InitCommand) -> Result<(), InitError> { +fn init_inner(_options: InitCommand) -> Result<(), Error> { unimplemented!("init command"); } diff --git a/src/cli/serve.rs b/src/cli/serve.rs index bc47f981..11e68baa 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -3,12 +3,11 @@ use std::{ sync::Arc, }; -use failure::Fail; +use snafu::Snafu; use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; use crate::{ cli::ServeCommand, - project::ProjectError, serve_session::ServeSession, vfs::{RealFetcher, Vfs, WatchMode}, web::LiveServer, @@ -16,17 +15,17 @@ use crate::{ const DEFAULT_PORT: u16 = 34872; -#[derive(Debug, Fail)] -pub enum ServeError { - #[fail(display = "Couldn't load project: {}", _0)] - ProjectError(#[fail(cause)] ProjectError), -} +#[derive(Debug, Snafu)] +pub struct ServeError(Error); -impl_from!(ServeError { - ProjectError => ProjectError, -}); +#[derive(Debug, Snafu)] +enum Error {} pub fn serve(options: ServeCommand) -> Result<(), ServeError> { + Ok(serve_inner(options)?) +} + +fn serve_inner(options: ServeCommand) -> Result<(), Error> { let vfs = Vfs::new(RealFetcher::new(WatchMode::Enabled)); let session = Arc::new(ServeSession::new(vfs, &options.project)); diff --git a/src/cli/upload.rs b/src/cli/upload.rs index df6f8429..4e14708a 100644 --- a/src/cli/upload.rs +++ b/src/cli/upload.rs @@ -1,5 +1,5 @@ -use failure::Fail; use reqwest::header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT}; +use snafu::{ResultExt, Snafu}; use crate::{ auth_cookie::get_auth_cookie, @@ -8,31 +8,35 @@ use crate::{ vfs::{RealFetcher, Vfs, WatchMode}, }; -#[derive(Debug, Fail)] -pub enum UploadError { - #[fail(display = "Rojo could not find your Roblox auth cookie. Please pass one via --cookie.")] +#[derive(Debug, Snafu)] +pub struct UploadError(Error); + +#[derive(Debug, Snafu)] +enum Error { + #[snafu(display( + "Rojo could not find your Roblox auth cookie. Please pass one via --cookie.", + ))] NeedAuthCookie, - #[fail(display = "XML model file encode error: {}", _0)] - XmlModelEncode(#[fail(cause)] rbx_xml::EncodeError), + #[snafu(display("XML model file encode error: {}", source))] + XmlModel { source: rbx_xml::EncodeError }, - #[fail(display = "HTTP error: {}", _0)] - Http(#[fail(cause)] reqwest::Error), + #[snafu(display("HTTP error: {}", source))] + Http { source: reqwest::Error }, - #[fail(display = "Roblox API error: {}", _0)] - RobloxApi(String), + #[snafu(display("Roblox API error: {}", body))] + RobloxApi { body: String }, } -impl_from!(UploadError { - rbx_xml::EncodeError => XmlModelEncode, - reqwest::Error => Http, -}); - pub fn upload(options: UploadCommand) -> Result<(), UploadError> { + Ok(upload_inner(options)?) +} + +fn upload_inner(options: UploadCommand) -> Result<(), Error> { let cookie = options .cookie .or_else(get_auth_cookie) - .ok_or(UploadError::NeedAuthCookie)?; + .ok_or(Error::NeedAuthCookie)?; log::trace!("Constructing in-memory filesystem"); let vfs = Vfs::new(RealFetcher::new(WatchMode::Disabled)); @@ -45,7 +49,7 @@ pub fn upload(options: UploadCommand) -> Result<(), UploadError> { log::trace!("Encoding XML model"); let config = rbx_xml::EncodeOptions::new() .property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown); - rbx_xml::to_writer(&mut buffer, tree.inner(), &[root_id], config)?; + rbx_xml::to_writer(&mut buffer, tree.inner(), &[root_id], config).context(XmlModel)?; let url = format!( "https://data.roblox.com/Data/Upload.ashx?assetid={}", @@ -62,10 +66,13 @@ pub fn upload(options: UploadCommand) -> Result<(), UploadError> { .header(CONTENT_TYPE, "application/xml") .header(ACCEPT, "application/json") .body(buffer) - .send()?; + .send() + .context(Http)?; if !response.status().is_success() { - return Err(UploadError::RobloxApi(response.text()?)); + return Err(Error::RobloxApi { + body: response.text().context(Http)?, + }); } Ok(()) diff --git a/src/impl_from.rs b/src/impl_from.rs deleted file mode 100644 index a1b4137a..00000000 --- a/src/impl_from.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Implements 'From' for a list of variants, intended for use with error enums -/// that are wrapping a number of errors from other methods. -macro_rules! impl_from { - ( - $enum_name: ident { - $($error_type: ty => $variant_name: ident),* $(,)* - } - ) => { - $( - impl From<$error_type> for $enum_name { - fn from(error: $error_type) -> $enum_name { - $enum_name::$variant_name(error) - } - } - )* - } -} diff --git a/src/lib.rs b/src/lib.rs index 4f74156d..a7ff62a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,6 @@ // Rojo's web UI currently. #![recursion_limit = "1024"] -#[macro_use] -mod impl_from; - pub mod cli; #[cfg(test)] diff --git a/src/vfs/error.rs b/src/vfs/error.rs index 989c64e2..54d4efb1 100644 --- a/src/vfs/error.rs +++ b/src/vfs/error.rs @@ -1,6 +1,4 @@ -use std::{fmt, io, path::PathBuf}; - -use failure::Fail; +use std::{error::Error, fmt, io, path::PathBuf}; pub type FsResult = Result; pub use io::ErrorKind as FsErrorKind; @@ -21,32 +19,37 @@ impl FsResultExt for Result { /// A wrapper around io::Error that also attaches the path associated with the /// error. -#[derive(Debug, Fail)] +#[derive(Debug)] pub struct FsError { - #[fail(cause)] - inner: io::Error, + source: io::Error, path: PathBuf, } impl FsError { - pub fn new>(inner: io::Error, path: P) -> FsError { + pub fn new>(source: io::Error, path: P) -> FsError { FsError { - inner, + source, path: path.into(), } } pub fn kind(&self) -> FsErrorKind { - self.inner.kind() + self.source.kind() } pub fn into_raw(self) -> (io::Error, PathBuf) { - (self.inner, self.path) + (self.source, self.path) + } +} + +impl Error for FsError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) } } impl fmt::Display for FsError { fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { - write!(output, "{}: {}", self.path.display(), self.inner) + write!(output, "{}: {}", self.path.display(), self.source) } }