forked from rojo-rbx/rojo
Switch everything to StructOpt (#277)
* Add types for Rojo's subcommands * Flesh out CLI types * Port everything to structopt instead of clap
This commit is contained in:
committed by
GitHub
parent
8b1e85fbb4
commit
47c7f63d75
51
Cargo.lock
generated
51
Cargo.lock
generated
@@ -602,6 +602,14 @@ dependencies = [
|
||||
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.3"
|
||||
@@ -1169,6 +1177,16 @@ dependencies = [
|
||||
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.11"
|
||||
@@ -1588,7 +1606,6 @@ dependencies = [
|
||||
name = "rojo"
|
||||
version = "0.6.0-dev"
|
||||
dependencies = [
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
@@ -1617,6 +1634,7 @@ dependencies = [
|
||||
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1850,6 +1868,27 @@ name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.11"
|
||||
@@ -2170,6 +2209,11 @@ dependencies = [
|
||||
"smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
@@ -2414,6 +2458,7 @@ dependencies = [
|
||||
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
|
||||
"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
||||
"checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0"
|
||||
@@ -2477,6 +2522,7 @@ dependencies = [
|
||||
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||
"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097"
|
||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
@@ -2543,6 +2589,8 @@ dependencies = [
|
||||
"checksum snax 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef82be0baf3ae45701ab992230f1f4889c15984736d87f37558aea3e4e321af"
|
||||
"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3a3e93f5ad553c38b3301c8a0a0cec829a36783f6a0c467fc4bf553a5f5bf"
|
||||
"checksum structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e"
|
||||
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
@@ -2574,6 +2622,7 @@ dependencies = [
|
||||
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf"
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
@@ -48,7 +48,6 @@ name = "build"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.27"
|
||||
crossbeam-channel = "0.3.9"
|
||||
csv = "1.0"
|
||||
env_logger = "0.6"
|
||||
@@ -57,6 +56,7 @@ futures = "0.1"
|
||||
humantime = "1.3.0"
|
||||
hyper = "0.12"
|
||||
jod-thread = "0.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
maplit = "1.0.1"
|
||||
notify = "4.0"
|
||||
@@ -70,6 +70,7 @@ ritz = "0.1.0"
|
||||
rlua = "0.16.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
structopt = "0.3"
|
||||
termcolor = "1.0.5"
|
||||
uuid = { version = "0.7", features = ["v4", "serde"] }
|
||||
|
||||
|
||||
200
src/bin.rs
200
src/bin.rs
@@ -1,63 +1,19 @@
|
||||
use std::{
|
||||
env, panic,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
use std::{panic, process};
|
||||
|
||||
use failure::Error;
|
||||
use log::error;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use librojo::{
|
||||
cli::{Options, Subcommand},
|
||||
commands,
|
||||
};
|
||||
|
||||
use clap::{clap_app, ArgMatches};
|
||||
use log::error;
|
||||
|
||||
use librojo::commands;
|
||||
|
||||
fn make_path_absolute(value: &Path) -> PathBuf {
|
||||
if value.is_absolute() {
|
||||
PathBuf::from(value)
|
||||
} else {
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
current_dir.join(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = clap_app!(Rojo =>
|
||||
(version: env!("CARGO_PKG_VERSION"))
|
||||
(author: env!("CARGO_PKG_AUTHORS"))
|
||||
(about: env!("CARGO_PKG_DESCRIPTION"))
|
||||
|
||||
(@arg verbose: --verbose -v +multiple +global "Sets verbosity level. Can be specified multiple times.")
|
||||
|
||||
(@subcommand init =>
|
||||
(about: "Creates a new Rojo project.")
|
||||
(@arg PATH: "Path to the place to create the project. Defaults to the current directory.")
|
||||
(@arg kind: --kind +takes_value "The kind of project to create, 'place' or 'model'. Defaults to place.")
|
||||
)
|
||||
|
||||
(@subcommand serve =>
|
||||
(about: "Serves the project's files for use with the Rojo Studio plugin.")
|
||||
(@arg PROJECT: "Path to the project to serve. Defaults to the current directory.")
|
||||
(@arg port: --port +takes_value "The port to listen on. Defaults to 34872.")
|
||||
)
|
||||
|
||||
(@subcommand build =>
|
||||
(about: "Generates a model or place file from the project.")
|
||||
(@arg PROJECT: "Path to the project to serve. Defaults to the current directory.")
|
||||
(@arg output: --output -o +takes_value +required "Where to output the result.")
|
||||
)
|
||||
|
||||
(@subcommand upload =>
|
||||
(about: "Generates a place or model file out of the project and uploads it to Roblox.")
|
||||
(@arg PROJECT: "Path to the project to upload. Defaults to the current directory.")
|
||||
(@arg kind: --kind +takes_value "The kind of asset to generate, 'place', or 'model'. Defaults to place.")
|
||||
(@arg cookie: --cookie +takes_value "Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.")
|
||||
(@arg asset_id: --asset_id +takes_value +required "Asset ID to upload to.")
|
||||
)
|
||||
);
|
||||
|
||||
let matches = app.get_matches();
|
||||
let options = Options::from_args();
|
||||
|
||||
{
|
||||
let verbosity = matches.occurrences_of("verbose");
|
||||
let log_filter = match verbosity {
|
||||
let log_filter = match options.verbosity {
|
||||
0 => "warn",
|
||||
1 => "warn,librojo=info",
|
||||
2 => "warn,librojo=trace",
|
||||
@@ -71,15 +27,14 @@ fn main() {
|
||||
.init();
|
||||
}
|
||||
|
||||
let result = panic::catch_unwind(|| match matches.subcommand() {
|
||||
("init", Some(sub_matches)) => start_init(sub_matches),
|
||||
("serve", Some(sub_matches)) => start_serve(sub_matches),
|
||||
("build", Some(sub_matches)) => start_build(sub_matches),
|
||||
("upload", Some(sub_matches)) => start_upload(sub_matches),
|
||||
_ => eprintln!("Usage: rojo <SUBCOMMAND>\nUse 'rojo help' for more help."),
|
||||
let panic_result = panic::catch_unwind(|| {
|
||||
if let Err(err) = run(options.subcommand) {
|
||||
log::error!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(error) = result {
|
||||
if let Err(error) = panic_result {
|
||||
let message = match error.downcast_ref::<&str>() {
|
||||
Some(message) => message.to_string(),
|
||||
None => match error.downcast_ref::<String>() {
|
||||
@@ -93,6 +48,17 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(subcommand: Subcommand) -> Result<(), Error> {
|
||||
match subcommand {
|
||||
Subcommand::Init(init_options) => commands::init(init_options)?,
|
||||
Subcommand::Serve(serve_options) => commands::serve(serve_options)?,
|
||||
Subcommand::Build(build_options) => commands::build(build_options)?,
|
||||
Subcommand::Upload(upload_options) => commands::upload(upload_options)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_crash_message(message: &str) {
|
||||
error!("Rojo crashed!");
|
||||
error!("This is a bug in Rojo.");
|
||||
@@ -101,113 +67,3 @@ fn show_crash_message(message: &str) {
|
||||
error!("");
|
||||
error!("Details: {}", message);
|
||||
}
|
||||
|
||||
fn start_init(sub_matches: &ArgMatches) {
|
||||
let fuzzy_project_path =
|
||||
make_path_absolute(Path::new(sub_matches.value_of("PATH").unwrap_or("")));
|
||||
let kind = sub_matches.value_of("kind");
|
||||
|
||||
let options = commands::InitOptions {
|
||||
fuzzy_project_path,
|
||||
kind,
|
||||
};
|
||||
|
||||
match commands::init(&options) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_serve(sub_matches: &ArgMatches) {
|
||||
let fuzzy_project_path = match sub_matches.value_of("PROJECT") {
|
||||
Some(v) => make_path_absolute(Path::new(v)),
|
||||
None => std::env::current_dir().unwrap(),
|
||||
};
|
||||
|
||||
let port = match sub_matches.value_of("port") {
|
||||
Some(v) => match v.parse::<u16>() {
|
||||
Ok(port) => Some(port),
|
||||
Err(_) => {
|
||||
error!("Invalid port {}", v);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let options = commands::ServeOptions {
|
||||
fuzzy_project_path,
|
||||
port,
|
||||
};
|
||||
|
||||
match commands::serve(&options) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_build(sub_matches: &ArgMatches) {
|
||||
let fuzzy_project_path = match sub_matches.value_of("PROJECT") {
|
||||
Some(v) => make_path_absolute(Path::new(v)),
|
||||
None => std::env::current_dir().unwrap(),
|
||||
};
|
||||
|
||||
let output_file = make_path_absolute(Path::new(sub_matches.value_of("output").unwrap()));
|
||||
|
||||
let options = commands::BuildOptions {
|
||||
fuzzy_project_path,
|
||||
output_file,
|
||||
output_kind: None, // TODO: Accept from argument
|
||||
};
|
||||
|
||||
match commands::build(&options) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_upload(sub_matches: &ArgMatches) {
|
||||
let fuzzy_project_path = match sub_matches.value_of("PROJECT") {
|
||||
Some(v) => make_path_absolute(Path::new(v)),
|
||||
None => std::env::current_dir().unwrap(),
|
||||
};
|
||||
|
||||
let kind = sub_matches.value_of("kind");
|
||||
let auth_cookie = sub_matches.value_of("cookie").map(Into::into);
|
||||
|
||||
let asset_id: u64 = {
|
||||
let arg = sub_matches.value_of("asset_id").unwrap();
|
||||
|
||||
match arg.parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
error!("Invalid place ID {}", arg);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let options = commands::UploadOptions {
|
||||
fuzzy_project_path,
|
||||
auth_cookie,
|
||||
asset_id,
|
||||
kind,
|
||||
};
|
||||
|
||||
match commands::upload(options) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
191
src/cli.rs
Normal file
191
src/cli.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
//! Defines Rojo's CLI through structopt types.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::{env, error::Error, fmt, path::PathBuf, str::FromStr};
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// Trick used with structopt to get the initial working directory of the
|
||||
/// process and store it for use in default values.
|
||||
fn working_dir() -> &'static str {
|
||||
lazy_static::lazy_static! {
|
||||
static ref INITIAL_WORKING_DIR: String = {
|
||||
env::current_dir().unwrap().display().to_string()
|
||||
};
|
||||
}
|
||||
|
||||
&INITIAL_WORKING_DIR
|
||||
}
|
||||
|
||||
/// Command line options that Rojo accepts, defined using the structopt crate.
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "Rojo", about, author)]
|
||||
pub struct Options {
|
||||
/// Sets verbosity level. Can be specified multiple times.
|
||||
#[structopt(long = "verbose", short, parse(from_occurrences))]
|
||||
pub verbosity: u8,
|
||||
|
||||
/// Subcommand to run in this invocation.
|
||||
#[structopt(subcommand)]
|
||||
pub subcommand: Subcommand,
|
||||
}
|
||||
|
||||
/// All of Rojo's subcommands.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum Subcommand {
|
||||
/// Creates a new Rojo project.
|
||||
Init(InitCommand),
|
||||
|
||||
/// Serves the project's files for use with the Rojo Studio plugin.
|
||||
Serve(ServeCommand),
|
||||
|
||||
/// Generates a model or place file from the project.
|
||||
Build(BuildCommand),
|
||||
|
||||
/// Generates a place or model file out of the project and uploads it to Roblox.
|
||||
Upload(UploadCommand),
|
||||
}
|
||||
|
||||
/// Initializes a new Rojo project.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct InitCommand {
|
||||
/// Path to the place to create the project. Defaults to the current directory.
|
||||
#[structopt(default_value = &working_dir())]
|
||||
pub path: PathBuf,
|
||||
|
||||
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
||||
#[structopt(long, default_value = "place")]
|
||||
pub kind: InitKind,
|
||||
}
|
||||
|
||||
/// The templates we support for initializing a Rojo project.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum InitKind {
|
||||
/// A place that matches what File -> New does in Roblox Studio.
|
||||
Place,
|
||||
|
||||
/// An empty model, suitable for a library or plugin.
|
||||
Model,
|
||||
}
|
||||
|
||||
impl FromStr for InitKind {
|
||||
type Err = InitKindParseError;
|
||||
|
||||
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
||||
match source {
|
||||
"place" => Ok(InitKind::Place),
|
||||
"model" => Ok(InitKind::Model),
|
||||
_ => Err(InitKindParseError {
|
||||
attempted: source.to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for failing to parse an `InitKind`.
|
||||
#[derive(Debug)]
|
||||
pub struct InitKindParseError {
|
||||
attempted: String,
|
||||
}
|
||||
|
||||
impl Error for InitKindParseError {}
|
||||
|
||||
impl fmt::Display for InitKindParseError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
formatter,
|
||||
"Invalid init kind '{}'. Valid kinds are: place, model",
|
||||
self.attempted
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Expose a Rojo project through a web server that can communicate with the
|
||||
/// Rojo Roblox Studio plugin, or be visited by the user in the browser.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct ServeCommand {
|
||||
/// Path to the project to serve. Defaults to the current directory.
|
||||
#[structopt(default_value = &working_dir())]
|
||||
pub project: PathBuf,
|
||||
|
||||
/// The port to listen on. Defaults to the project's preference, or 34872 if
|
||||
/// it has none.
|
||||
#[structopt(long)]
|
||||
pub port: Option<u16>,
|
||||
}
|
||||
|
||||
/// Build a Rojo project into a file.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct BuildCommand {
|
||||
/// Path to the project to serve. Defaults to the current directory.
|
||||
#[structopt(default_value = &working_dir())]
|
||||
pub project: PathBuf,
|
||||
|
||||
/// Where to output the result.
|
||||
#[structopt(long, short)]
|
||||
pub output: PathBuf,
|
||||
}
|
||||
|
||||
/// Build and upload a Rojo project to Roblox.com.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct UploadCommand {
|
||||
/// Path to the project to upload. Defaults to the current directory.
|
||||
#[structopt(default_value = &working_dir())]
|
||||
pub project: PathBuf,
|
||||
|
||||
/// The kind of asset to generate, 'place', or 'model'. Defaults to place.
|
||||
#[structopt(long, default_value = "place")]
|
||||
pub kind: UploadKind,
|
||||
|
||||
/// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.
|
||||
#[structopt(long)]
|
||||
pub cookie: Option<String>,
|
||||
|
||||
/// Asset ID to upload to.
|
||||
#[structopt(long = "asset_id")]
|
||||
pub asset_id: u64,
|
||||
}
|
||||
|
||||
/// 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<Self, Self::Err> {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,27 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
use crate::{
|
||||
cli::BuildCommand,
|
||||
common_setup,
|
||||
project::ProjectLoadError,
|
||||
vfs::{FsError, RealFetcher, Vfs, WatchMode},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OutputKind {
|
||||
enum OutputKind {
|
||||
Rbxmx,
|
||||
Rbxlx,
|
||||
Rbxm,
|
||||
Rbxl,
|
||||
}
|
||||
|
||||
fn detect_output_kind(options: &BuildOptions) -> Option<OutputKind> {
|
||||
let extension = options.output_file.extension()?.to_str()?;
|
||||
fn detect_output_kind(options: &BuildCommand) -> Option<OutputKind> {
|
||||
let extension = options.output.extension()?.to_str()?;
|
||||
|
||||
match extension {
|
||||
"rbxlx" => Some(OutputKind::Rbxlx),
|
||||
@@ -32,13 +32,6 @@ fn detect_output_kind(options: &BuildOptions) -> Option<OutputKind> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BuildOptions {
|
||||
pub fuzzy_project_path: PathBuf,
|
||||
pub output_file: PathBuf,
|
||||
pub output_kind: Option<OutputKind>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum BuildError {
|
||||
#[fail(display = "Could not detect what kind of file to create")]
|
||||
@@ -72,22 +65,19 @@ fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||
}
|
||||
|
||||
pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
let output_kind = options
|
||||
.output_kind
|
||||
.or_else(|| detect_output_kind(options))
|
||||
.ok_or(BuildError::UnknownOutputKind)?;
|
||||
pub fn build(options: BuildCommand) -> Result<(), BuildError> {
|
||||
let output_kind = detect_output_kind(&options).ok_or(BuildError::UnknownOutputKind)?;
|
||||
|
||||
log::debug!("Hoping to generate file of type {:?}", output_kind);
|
||||
|
||||
log::trace!("Constructing in-memory filesystem");
|
||||
let vfs = Vfs::new(RealFetcher::new(WatchMode::Disabled));
|
||||
|
||||
let (_maybe_project, tree) = common_setup::start(&options.fuzzy_project_path, &vfs);
|
||||
let (_maybe_project, tree) = common_setup::start(&options.project, &vfs);
|
||||
let root_id = tree.get_root_id();
|
||||
|
||||
log::trace!("Opening output file for write");
|
||||
let mut file = BufWriter::new(File::create(&options.output_file)?);
|
||||
let mut file = BufWriter::new(File::create(&options.output)?);
|
||||
|
||||
match output_kind {
|
||||
OutputKind::Rbxmx => {
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
use crate::project::{Project, ProjectInitError};
|
||||
use crate::{
|
||||
cli::{InitCommand, InitKind},
|
||||
project::{Project, ProjectInitError},
|
||||
};
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum InitError {
|
||||
#[fail(
|
||||
display = "Invalid project kind '{}', valid kinds are 'place' and 'model'",
|
||||
_0
|
||||
)]
|
||||
InvalidKind(String),
|
||||
|
||||
#[fail(display = "Project init error: {}", _0)]
|
||||
ProjectInitError(#[fail(cause)] ProjectInitError),
|
||||
}
|
||||
@@ -20,23 +15,16 @@ impl_from!(InitError {
|
||||
ProjectInitError => ProjectInitError,
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InitOptions<'a> {
|
||||
pub fuzzy_project_path: PathBuf,
|
||||
pub kind: Option<&'a str>,
|
||||
}
|
||||
|
||||
pub fn init(options: &InitOptions) -> Result<(), InitError> {
|
||||
pub fn init(options: InitCommand) -> Result<(), InitError> {
|
||||
let (project_path, project_kind) = match options.kind {
|
||||
Some("place") | None => {
|
||||
let path = Project::init_place(&options.fuzzy_project_path)?;
|
||||
InitKind::Place => {
|
||||
let path = Project::init_place(&options.path)?;
|
||||
(path, "place")
|
||||
}
|
||||
Some("model") => {
|
||||
let path = Project::init_model(&options.fuzzy_project_path)?;
|
||||
InitKind::Model => {
|
||||
let path = Project::init_model(&options.path)?;
|
||||
(path, "model")
|
||||
}
|
||||
Some(invalid) => return Err(InitError::InvalidKind(invalid.to_string())),
|
||||
};
|
||||
|
||||
println!(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@@ -8,6 +7,7 @@ use failure::Fail;
|
||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||
|
||||
use crate::{
|
||||
cli::ServeCommand,
|
||||
project::ProjectLoadError,
|
||||
serve_session::ServeSession,
|
||||
vfs::{RealFetcher, Vfs, WatchMode},
|
||||
@@ -16,12 +16,6 @@ use crate::{
|
||||
|
||||
const DEFAULT_PORT: u16 = 34872;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServeOptions {
|
||||
pub fuzzy_project_path: PathBuf,
|
||||
pub port: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ServeError {
|
||||
#[fail(display = "Couldn't load project: {}", _0)]
|
||||
@@ -32,10 +26,10 @@ impl_from!(ServeError {
|
||||
ProjectLoadError => ProjectLoad,
|
||||
});
|
||||
|
||||
pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
pub fn serve(options: ServeCommand) -> Result<(), ServeError> {
|
||||
let vfs = Vfs::new(RealFetcher::new(WatchMode::Enabled));
|
||||
|
||||
let session = Arc::new(ServeSession::new(vfs, &options.fuzzy_project_path));
|
||||
let session = Arc::new(ServeSession::new(vfs, &options.project));
|
||||
|
||||
let port = options
|
||||
.port
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use failure::Fail;
|
||||
use reqwest::header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT};
|
||||
|
||||
use crate::{
|
||||
auth_cookie::get_auth_cookie,
|
||||
cli::UploadCommand,
|
||||
common_setup,
|
||||
vfs::{RealFetcher, Vfs, WatchMode},
|
||||
};
|
||||
@@ -29,24 +28,16 @@ impl_from!(UploadError {
|
||||
reqwest::Error => Http,
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UploadOptions<'a> {
|
||||
pub fuzzy_project_path: PathBuf,
|
||||
pub auth_cookie: Option<String>,
|
||||
pub asset_id: u64,
|
||||
pub kind: Option<&'a str>,
|
||||
}
|
||||
|
||||
pub fn upload(options: UploadOptions) -> Result<(), UploadError> {
|
||||
pub fn upload(options: UploadCommand) -> Result<(), UploadError> {
|
||||
let cookie = options
|
||||
.auth_cookie
|
||||
.cookie
|
||||
.or_else(get_auth_cookie)
|
||||
.ok_or(UploadError::NeedAuthCookie)?;
|
||||
|
||||
log::trace!("Constructing in-memory filesystem");
|
||||
let vfs = Vfs::new(RealFetcher::new(WatchMode::Disabled));
|
||||
|
||||
let (_maybe_project, tree) = common_setup::start(&options.fuzzy_project_path, &vfs);
|
||||
let (_maybe_project, tree) = common_setup::start(&options.project, &vfs);
|
||||
let root_id = tree.get_root_id();
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#[macro_use]
|
||||
mod impl_from;
|
||||
|
||||
pub mod cli;
|
||||
pub mod commands;
|
||||
|
||||
// This module is only public for testing right now, and won't be
|
||||
|
||||
Reference in New Issue
Block a user