From d0482a004e5e281fcc712ef83d5ef48709900b2d Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Tue, 8 Jun 2021 21:53:56 -0400 Subject: [PATCH] Modernize upload command --- src/cli/mod.rs | 78 ++--------------------------------- src/cli/upload.rs | 103 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 106 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 30081d77..cace812c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -8,14 +8,7 @@ mod plugin; mod serve; mod upload; -use std::{ - borrow::Cow, - env, - error::Error, - fmt, - path::{Path, PathBuf}, - str::FromStr, -}; +use std::{borrow::Cow, env, path::Path, str::FromStr}; use structopt::StructOpt; use thiserror::Error; @@ -26,7 +19,7 @@ pub use self::fmt_project::FmtProjectCommand; pub use self::init::{InitCommand, InitKind}; pub use self::plugin::{PluginCommand, PluginSubcommand}; pub use self::serve::ServeCommand; -pub use self::upload::*; +pub use self::upload::UploadCommand; /// Command line options that Rojo accepts, defined using the structopt crate. #[derive(Debug, StructOpt)] @@ -46,7 +39,7 @@ impl Options { Subcommand::Init(subcommand) => subcommand.run(), Subcommand::Serve(subcommand) => subcommand.run(self.global), Subcommand::Build(subcommand) => subcommand.run(), - Subcommand::Upload(upload_options) => upload(upload_options), + Subcommand::Upload(subcommand) => subcommand.run(), Subcommand::FmtProject(subcommand) => subcommand.run(), Subcommand::Doc(subcommand) => subcommand.run(), Subcommand::Plugin(subcommand) => subcommand.run(), @@ -124,71 +117,6 @@ pub enum Subcommand { Plugin(PluginCommand), } -/// Builds the project and uploads it to Roblox. -#[derive(Debug, StructOpt)] -pub struct UploadCommand { - /// Path to the project to upload. Defaults to the current directory. - #[structopt(default_value = "")] - pub project: PathBuf, - - /// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically. - #[structopt(long)] - pub cookie: Option, - - /// Asset ID to upload to. - #[structopt(long = "asset_id")] - pub asset_id: u64, -} - -impl UploadCommand { - pub fn absolute_project(&self) -> Cow<'_, Path> { - resolve_path(&self.project) - } -} - -/// The kind of asset to upload to the website. Affects what endpoints Rojo uses -/// and changes how the asset is built. -#[derive(Debug, Clone, Copy)] -pub enum UploadKind { - /// Upload to a place. - Place, - - /// Upload to a model-like asset, like a Model, Plugin, or Package. - Model, -} - -impl FromStr for UploadKind { - type Err = UploadKindParseError; - - fn from_str(source: &str) -> Result { - match source { - "place" => Ok(UploadKind::Place), - "model" => Ok(UploadKind::Model), - _ => Err(UploadKindParseError { - attempted: source.to_owned(), - }), - } - } -} - -/// Error type for failing to parse an `UploadKind`. -#[derive(Debug)] -pub struct UploadKindParseError { - attempted: String, -} - -impl Error for UploadKindParseError {} - -impl fmt::Display for UploadKindParseError { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!( - formatter, - "Invalid upload kind '{}'. Valid kinds are: place, model", - self.attempted - ) - } -} - pub(super) fn resolve_path(path: &Path) -> Cow<'_, Path> { if path.is_absolute() { Cow::Borrowed(path) diff --git a/src/cli/upload.rs b/src/cli/upload.rs index 0846e6bc..c1d7217b 100644 --- a/src/cli/upload.rs +++ b/src/cli/upload.rs @@ -1,46 +1,87 @@ +use std::path::PathBuf; +use std::str::FromStr; + +use anyhow::{bail, format_err, Context}; use memofs::Vfs; use reqwest::{ header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT}, StatusCode, }; -use thiserror::Error; +use structopt::StructOpt; -use crate::{auth_cookie::get_auth_cookie, cli::UploadCommand, serve_session::ServeSession}; +use crate::{auth_cookie::get_auth_cookie, serve_session::ServeSession}; -#[derive(Debug, Error)] -enum Error { - #[error("Rojo could not find your Roblox auth cookie. Please pass one via --cookie.")] - NeedAuthCookie, +use super::resolve_path; - #[error("The Roblox API returned an unexpected error: {body}")] - RobloxApi { body: String }, +/// Builds the project and uploads it to Roblox. +#[derive(Debug, StructOpt)] +pub struct UploadCommand { + /// Path to the project to upload. Defaults to the current directory. + #[structopt(default_value = "")] + pub project: PathBuf, + + /// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically. + #[structopt(long)] + pub cookie: Option, + + /// Asset ID to upload to. + #[structopt(long = "asset_id")] + pub asset_id: u64, } -pub fn upload(options: UploadCommand) -> Result<(), anyhow::Error> { - let cookie = options - .cookie - .clone() - .or_else(get_auth_cookie) - .ok_or(Error::NeedAuthCookie)?; +impl UploadCommand { + pub fn run(self) -> Result<(), anyhow::Error> { + let project_path = resolve_path(&self.project); - let vfs = Vfs::new_default(); + let cookie = self.cookie.or_else(get_auth_cookie).context( + "Rojo could not find your Roblox auth cookie. Please pass one via --cookie.", + )?; - let session = ServeSession::new(vfs, &options.absolute_project())?; + let vfs = Vfs::new_default(); - let tree = session.tree(); - let inner_tree = tree.inner(); - let root = inner_tree.root(); + let session = ServeSession::new(vfs, project_path)?; - let encode_ids = match root.class.as_str() { - "DataModel" => root.children().to_vec(), - _ => vec![root.referent()], - }; + let tree = session.tree(); + let inner_tree = tree.inner(); + let root = inner_tree.root(); - let mut buffer = Vec::new(); + let encode_ids = match root.class.as_str() { + "DataModel" => root.children().to_vec(), + _ => vec![root.referent()], + }; - log::trace!("Encoding binary model"); - rbx_binary::to_writer_default(&mut buffer, tree.inner(), &encode_ids)?; - do_upload(buffer, options.asset_id, &cookie) + let mut buffer = Vec::new(); + + log::trace!("Encoding binary model"); + rbx_binary::to_writer_default(&mut buffer, tree.inner(), &encode_ids)?; + do_upload(buffer, self.asset_id, &cookie) + } +} + +/// The kind of asset to upload to the website. Affects what endpoints Rojo uses +/// and changes how the asset is built. +#[derive(Debug, Clone, Copy)] +enum UploadKind { + /// Upload to a place. + Place, + + /// Upload to a model-like asset, like a Model, Plugin, or Package. + Model, +} + +impl FromStr for UploadKind { + type Err = anyhow::Error; + + fn from_str(source: &str) -> Result { + match source { + "place" => Ok(UploadKind::Place), + "model" => Ok(UploadKind::Model), + attempted => Err(format_err!( + "Invalid upload kind '{}'. Valid kinds are: place, model", + attempted + )), + } + } } fn do_upload(buffer: Vec, asset_id: u64, cookie: &str) -> anyhow::Result<()> { @@ -76,10 +117,10 @@ fn do_upload(buffer: Vec, asset_id: u64, cookie: &str) -> anyhow::Result<()> let status = response.status(); if !status.is_success() { - return Err(Error::RobloxApi { - body: response.text()?, - } - .into()); + bail!( + "The Roblox API returned an unexpected error: {}", + response.text()? + ); } Ok(())