mirror of
https://github.com/macalimlim/ftw
synced 2024-11-10 12:34:11 +00:00
add new command
This commit is contained in:
parent
bb08c87b66
commit
c6ffd9a871
9 changed files with 278 additions and 1 deletions
|
@ -7,3 +7,8 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = "3.0.0-beta.2"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "1.0"
|
||||
predicates = "1.0"
|
||||
|
|
16
README.md
16
README.md
|
@ -1 +1,17 @@
|
|||
# ftw
|
||||
|
||||
## Commands
|
||||
|
||||
### new
|
||||
|
||||
creates a new godot-rust project
|
||||
|
||||
```shell
|
||||
|
||||
$ ftw new my-awesome-game # creates a godot-rust project using the default template
|
||||
|
||||
$ ftw new my-awesome-game default # same as above
|
||||
|
||||
$ ftw new my-awesome-game /path/to/some/template # creates a godot-rust project using a custom template
|
||||
|
||||
```
|
||||
|
|
45
src/ftw_command.rs
Normal file
45
src/ftw_command.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use crate::ftw_template::FtwTemplate;
|
||||
use crate::process_command::ProcessCommand;
|
||||
use crate::traits::Processor;
|
||||
use crate::traits::ToGitUrl;
|
||||
use crate::type_alias::Commands;
|
||||
use crate::type_alias::ProjectName;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
|
||||
pub enum FtwCommand {
|
||||
New {
|
||||
project_name: ProjectName,
|
||||
template: FtwTemplate,
|
||||
},
|
||||
}
|
||||
|
||||
impl Processor for FtwCommand {
|
||||
fn process(&self) {
|
||||
match self {
|
||||
FtwCommand::New {
|
||||
project_name,
|
||||
template,
|
||||
} => {
|
||||
let git_url = &template.to_git_url();
|
||||
let gitignore_path: String = format!("{}/.gitignore", project_name);
|
||||
let commands: Commands = vec![vec![
|
||||
"cargo",
|
||||
"generate",
|
||||
"--name",
|
||||
project_name,
|
||||
"--git",
|
||||
git_url,
|
||||
]];
|
||||
(ProcessCommand { commands: commands }).process();
|
||||
let mut gitignore_file = OpenOptions::new()
|
||||
.append(true)
|
||||
.open(gitignore_path)
|
||||
.unwrap();
|
||||
if let Err(e) = writeln!(gitignore_file, "godot/export_presets.cfg") {
|
||||
eprintln!("Couldn't write to file: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
src/ftw_template.rs
Normal file
30
src/ftw_template.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::traits::ToGitUrl;
|
||||
use crate::type_alias::GitUrl;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub enum FtwTemplate {
|
||||
Default,
|
||||
Custom { git_url: GitUrl },
|
||||
}
|
||||
|
||||
impl FromStr for FtwTemplate {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"default" => Ok(FtwTemplate::Default),
|
||||
git_url => Ok(FtwTemplate::Custom {
|
||||
git_url: git_url.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToGitUrl for FtwTemplate {
|
||||
fn to_git_url(&self) -> GitUrl {
|
||||
match self {
|
||||
FtwTemplate::Default => "https://github.com/godot-rust/godot-rust-template",
|
||||
FtwTemplate::Custom { git_url } => git_url,
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
40
src/main.rs
40
src/main.rs
|
@ -1,3 +1,41 @@
|
|||
mod ftw_command;
|
||||
mod ftw_template;
|
||||
mod process_command;
|
||||
mod traits;
|
||||
mod type_alias;
|
||||
|
||||
use crate::ftw_command::FtwCommand;
|
||||
use crate::ftw_template::FtwTemplate;
|
||||
use crate::traits::Processor;
|
||||
use clap::{clap_app, crate_authors, crate_version};
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let version = crate_version!();
|
||||
let author = crate_authors!("\n");
|
||||
let matches = clap_app!(ftw =>
|
||||
(version: version)
|
||||
(author: author)
|
||||
(about: "manage your godot-rust project")
|
||||
(@subcommand new =>
|
||||
(about: "create a new godot-rust project directory")
|
||||
(@arg project_name: +required "set the name of your project")
|
||||
(@arg template: !required "set the template to be used in your project")))
|
||||
.get_matches();
|
||||
let command: FtwCommand = match matches.subcommand() {
|
||||
Some(("new", args)) => {
|
||||
let template: FtwTemplate = match args
|
||||
.value_of("template")
|
||||
.and_then(|template| Some(template))
|
||||
{
|
||||
Some(template) => template.parse().unwrap(),
|
||||
None => FtwTemplate::Default,
|
||||
};
|
||||
FtwCommand::New {
|
||||
project_name: args.value_of("project_name").unwrap().to_string(),
|
||||
template: template,
|
||||
}
|
||||
}
|
||||
_ => panic!("this should not happen!"),
|
||||
};
|
||||
command.process();
|
||||
}
|
||||
|
|
28
src/process_command.rs
Normal file
28
src/process_command.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use crate::traits::Processor;
|
||||
use crate::type_alias::Commands;
|
||||
use std::io::{self, Write};
|
||||
use std::process::Command;
|
||||
|
||||
pub struct ProcessCommand<'a> {
|
||||
pub commands: Commands<'a>,
|
||||
}
|
||||
|
||||
impl Processor for ProcessCommand<'_> {
|
||||
fn process(&self) {
|
||||
for xs in &self.commands {
|
||||
let out = match xs.split_at(1) {
|
||||
(&[cmd], args) => args
|
||||
.iter()
|
||||
.fold(&mut Command::new(cmd), |s, i| s.arg(i))
|
||||
.output()
|
||||
.expect("failed to execute process"),
|
||||
_ => panic!("this should not happen"),
|
||||
};
|
||||
if out.status.success() {
|
||||
io::stdout().write_all(&out.stdout).unwrap();
|
||||
} else {
|
||||
io::stderr().write_all(&out.stderr).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/traits.rs
Normal file
9
src/traits.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use crate::type_alias::GitUrl;
|
||||
|
||||
pub trait Processor {
|
||||
fn process(&self);
|
||||
}
|
||||
|
||||
pub trait ToGitUrl {
|
||||
fn to_git_url(&self) -> GitUrl;
|
||||
}
|
3
src/type_alias.rs
Normal file
3
src/type_alias.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub type Commands<'a> = Vec<Vec<&'a str>>;
|
||||
pub type GitUrl = String;
|
||||
pub type ProjectName = String;
|
103
tests/test_main.rs
Normal file
103
tests/test_main.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use assert_cmd::prelude::*;
|
||||
use predicates::prelude::*;
|
||||
use std::env;
|
||||
use std::fs::{remove_dir_all, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
const GAME: &str = "my-awesome-game";
|
||||
|
||||
fn ftw() -> std::process::Command {
|
||||
Command::cargo_bin("ftw").unwrap()
|
||||
}
|
||||
|
||||
fn setup() {}
|
||||
|
||||
fn teardown() {
|
||||
remove_dir_all(GAME);
|
||||
}
|
||||
|
||||
fn test_generated_project() {
|
||||
assert!(Path::new(GAME).exists());
|
||||
let gitignore_file_path = format!("{}/.gitignore", GAME);
|
||||
let mut gitignore_file = File::open(gitignore_file_path).unwrap();
|
||||
let mut gitignore_file_contents = String::new();
|
||||
gitignore_file
|
||||
.read_to_string(&mut gitignore_file_contents)
|
||||
.unwrap();
|
||||
assert!(gitignore_file_contents.contains("export_presets.cfg"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ftw_new() -> Result<(), Box<dyn std::error::Error>> {
|
||||
setup();
|
||||
let current_path = env::current_dir()?;
|
||||
ftw()
|
||||
.arg("new")
|
||||
.arg(GAME)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(format!(
|
||||
"Done! New project created {}/{}",
|
||||
current_path.display(),
|
||||
GAME
|
||||
)));
|
||||
test_generated_project();
|
||||
teardown();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ftw_new_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
setup();
|
||||
ftw()
|
||||
.arg("new")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"The following required arguments were not provided:\n <project_name>\n",
|
||||
));
|
||||
teardown();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ftw_new_with_default_template() -> Result<(), Box<dyn std::error::Error>> {
|
||||
setup();
|
||||
let current_path = env::current_dir()?;
|
||||
ftw()
|
||||
.arg("new")
|
||||
.arg(GAME)
|
||||
.arg("default")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(format!(
|
||||
"Done! New project created {}/{}",
|
||||
current_path.display(),
|
||||
GAME
|
||||
)));
|
||||
test_generated_project();
|
||||
teardown();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ftw_new_with_custom_template() -> Result<(), Box<dyn std::error::Error>> {
|
||||
setup();
|
||||
let current_path = env::current_dir()?;
|
||||
ftw()
|
||||
.arg("new")
|
||||
.arg(GAME)
|
||||
.arg("../ftw")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(format!(
|
||||
"Done! New project created {}/{}",
|
||||
current_path.display(),
|
||||
GAME
|
||||
)));
|
||||
test_generated_project();
|
||||
teardown();
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue