mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-25 23:26:19 +00:00
Modernize the init subcommand
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
* Empty `.model.json` files will no longer cause errors. ([#420][pr-420])
|
* Empty `.model.json` files will no longer cause errors. ([#420][pr-420])
|
||||||
* When specifying `$path` on a service, Rojo now keeps the correct class name. ([#331][issue-331])
|
* When specifying `$path` on a service, Rojo now keeps the correct class name. ([#331][issue-331])
|
||||||
* Improved error messages for misconfigured projects.
|
* Improved error messages for misconfigured projects.
|
||||||
|
* Improved error output for many subcommands.
|
||||||
|
|
||||||
[issue-331]: https://github.com/rojo-rbx/rojo/issues/331
|
[issue-331]: https://github.com/rojo-rbx/rojo/issues/331
|
||||||
[issue-369]: https://github.com/rojo-rbx/rojo/issues/369
|
[issue-369]: https://github.com/rojo-rbx/rojo/issues/369
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use librojo::cli::{self, GlobalOptions, Options, Subcommand};
|
|||||||
|
|
||||||
fn run(global: GlobalOptions, subcommand: Subcommand) -> anyhow::Result<()> {
|
fn run(global: GlobalOptions, subcommand: Subcommand) -> anyhow::Result<()> {
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Subcommand::Init(init_options) => cli::init(init_options)?,
|
Subcommand::Init(subcommand) => subcommand.run()?,
|
||||||
Subcommand::Serve(serve_options) => cli::serve(global, serve_options)?,
|
Subcommand::Serve(serve_options) => cli::serve(global, serve_options)?,
|
||||||
Subcommand::Build(build_options) => cli::build(build_options)?,
|
Subcommand::Build(build_options) => cli::build(build_options)?,
|
||||||
Subcommand::Upload(upload_options) => cli::upload(upload_options)?,
|
Subcommand::Upload(upload_options) => cli::upload(upload_options)?,
|
||||||
Subcommand::FmtProject(fmt_options) => fmt_options.run()?,
|
Subcommand::FmtProject(subcommand) => subcommand.run()?,
|
||||||
Subcommand::Doc => cli::doc()?,
|
Subcommand::Doc => cli::doc()?,
|
||||||
Subcommand::Plugin(plugin_options) => cli::plugin(plugin_options)?,
|
Subcommand::Plugin(plugin_options) => cli::plugin(plugin_options)?,
|
||||||
}
|
}
|
||||||
|
|||||||
111
src/cli/init.rs
111
src/cli/init.rs
@@ -1,13 +1,14 @@
|
|||||||
use std::{
|
use std::io::{self, Write};
|
||||||
fs::{self, OpenOptions},
|
use std::path::{Path, PathBuf};
|
||||||
io::{self, Write},
|
use std::process::{Command, Stdio};
|
||||||
path::Path,
|
use std::str::FromStr;
|
||||||
process::{Command, Stdio},
|
|
||||||
};
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use anyhow::{bail, format_err};
|
||||||
|
use fs_err as fs;
|
||||||
|
use fs_err::OpenOptions;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::cli::{InitCommand, InitKind};
|
use super::resolve_path;
|
||||||
|
|
||||||
static MODEL_PROJECT: &str =
|
static MODEL_PROJECT: &str =
|
||||||
include_str!("../../assets/default-model-project/default.project.json");
|
include_str!("../../assets/default-model-project/default.project.json");
|
||||||
@@ -20,37 +21,71 @@ static PLACE_PROJECT: &str =
|
|||||||
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
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");
|
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
/// Initializes a new Rojo project.
|
||||||
enum Error {
|
#[derive(Debug, StructOpt)]
|
||||||
#[error("A project file named default.project.json already exists in this folder")]
|
pub struct InitCommand {
|
||||||
AlreadyExists,
|
/// Path to the place to create the project. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub path: PathBuf,
|
||||||
|
|
||||||
#[error("git init failed")]
|
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
||||||
GitInit,
|
#[structopt(long, default_value = "place")]
|
||||||
|
pub kind: InitKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(options: InitCommand) -> Result<(), anyhow::Error> {
|
impl InitCommand {
|
||||||
let base_path = options.absolute_path();
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
fs::create_dir_all(&base_path)?;
|
let base_path = resolve_path(&self.path);
|
||||||
|
fs::create_dir_all(&base_path)?;
|
||||||
|
|
||||||
let canonical = fs::canonicalize(&base_path)?;
|
let canonical = fs::canonicalize(&base_path)?;
|
||||||
let project_name = canonical
|
let project_name = canonical
|
||||||
.file_name()
|
.file_name()
|
||||||
.and_then(|name| name.to_str())
|
.and_then(|name| name.to_str())
|
||||||
.unwrap_or("new-project");
|
.unwrap_or("new-project");
|
||||||
|
|
||||||
let project_params = ProjectParams {
|
let project_params = ProjectParams {
|
||||||
name: project_name.to_owned(),
|
name: project_name.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match options.kind {
|
match self.kind {
|
||||||
InitKind::Place => init_place(&base_path, project_params),
|
InitKind::Place => init_place(&base_path, project_params)?,
|
||||||
InitKind::Model => init_model(&base_path, project_params),
|
InitKind::Model => init_model(&base_path, project_params)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Created project successfully.");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
/// The templates we support for initializing a Rojo project.
|
||||||
eprintln!("Creating new place project '{}'", project_params.name);
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum InitKind {
|
||||||
|
/// A place that contains a baseplate.
|
||||||
|
Place,
|
||||||
|
|
||||||
|
/// An empty model, suitable for a library or plugin.
|
||||||
|
Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for InitKind {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
||||||
|
match source {
|
||||||
|
"place" => Ok(InitKind::Place),
|
||||||
|
"model" => Ok(InitKind::Model),
|
||||||
|
_ => Err(format_err!(
|
||||||
|
"Invalid init kind '{}'. Valid kinds are: place, model",
|
||||||
|
source
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
|
println!("Creating new place project '{}'", project_params.name);
|
||||||
|
|
||||||
let project_file = project_params.render_template(PLACE_PROJECT);
|
let project_file = project_params.render_template(PLACE_PROJECT);
|
||||||
try_create_project(base_path, &project_file)?;
|
try_create_project(base_path, &project_file)?;
|
||||||
@@ -88,13 +123,11 @@ fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), any
|
|||||||
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
||||||
try_git_init(base_path, &git_ignore)?;
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
eprintln!("Created project successfully.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
eprintln!("Creating new model project '{}'", project_params.name);
|
println!("Creating new model project '{}'", project_params.name);
|
||||||
|
|
||||||
let project_file = project_params.render_template(MODEL_PROJECT);
|
let project_file = project_params.render_template(MODEL_PROJECT);
|
||||||
try_create_project(base_path, &project_file)?;
|
try_create_project(base_path, &project_file)?;
|
||||||
@@ -111,8 +144,6 @@ fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), any
|
|||||||
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
||||||
try_git_init(base_path, &git_ignore)?;
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
eprintln!("Created project successfully.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +169,7 @@ fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::Error> {
|
|||||||
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err(Error::GitInit.into());
|
bail!("git init failed: status code {:?}", status.code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,13 +226,15 @@ fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::Er
|
|||||||
let file_res = OpenOptions::new()
|
let file_res = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.open(project_path);
|
.open(&project_path);
|
||||||
|
|
||||||
let mut file = match file_res {
|
let mut file = match file_res {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return match err.kind() {
|
return match err.kind() {
|
||||||
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists.into()),
|
io::ErrorKind::AlreadyExists => {
|
||||||
|
bail!("Project file already exists: {}", project_path.display())
|
||||||
|
}
|
||||||
_ => Err(err.into()),
|
_ => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use thiserror::Error;
|
|||||||
pub use self::build::*;
|
pub use self::build::*;
|
||||||
pub use self::doc::*;
|
pub use self::doc::*;
|
||||||
pub use self::fmt_project::FmtProjectCommand;
|
pub use self::fmt_project::FmtProjectCommand;
|
||||||
pub use self::init::*;
|
pub use self::init::{InitCommand, InitKind};
|
||||||
pub use self::plugin::*;
|
pub use self::plugin::*;
|
||||||
pub use self::serve::*;
|
pub use self::serve::*;
|
||||||
pub use self::upload::*;
|
pub use self::upload::*;
|
||||||
@@ -102,7 +102,6 @@ pub struct ColorChoiceParseError {
|
|||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
pub enum Subcommand {
|
pub enum Subcommand {
|
||||||
/// Creates a new Rojo project.
|
|
||||||
Init(InitCommand),
|
Init(InitCommand),
|
||||||
|
|
||||||
/// Serves the project's files for use with the Rojo Studio plugin.
|
/// Serves the project's files for use with the Rojo Studio plugin.
|
||||||
@@ -123,66 +122,6 @@ pub enum Subcommand {
|
|||||||
Plugin(PluginCommand),
|
Plugin(PluginCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 = "")]
|
|
||||||
pub path: PathBuf,
|
|
||||||
|
|
||||||
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
|
||||||
#[structopt(long, default_value = "place")]
|
|
||||||
pub kind: InitKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InitCommand {
|
|
||||||
pub fn absolute_path(&self) -> Cow<'_, Path> {
|
|
||||||
resolve_path(&self.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
/// 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.
|
/// Rojo Roblox Studio plugin, or be visited by the user in the browser.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
|
|||||||
Reference in New Issue
Block a user