forked from rojo-rbx/rojo
Use thiserror and anyhow for command-level error types
This commit is contained in:
@@ -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