cargo_ease/
lib.rs

1mod authors;
2mod emoji;
3mod git;
4mod template;
5
6use crate::git::GitConfig;
7use crate::template::Name;
8use cargo;
9use console::style;
10use dialoguer::Input;
11use failure;
12use quicli::prelude::Error;
13use std::env;
14use std::path::PathBuf;
15use structopt::StructOpt;
16
17static EE_TEMPLATE: &str = "https://github.com/quilt/ease-template.git"; //TODO: Switch to quilt repo
18
19#[derive(StructOpt)]
20#[structopt(bin_name = "cargo")]
21pub enum Cli {
22    #[structopt(name = "ease")]
23    Ease(Args),
24}
25
26#[derive(Debug, StructOpt)]
27pub struct Args {
28    #[structopt(long = "branch")]
29    branch: Option<String>,
30    #[structopt(long = "name", short = "n")]
31    name: Option<String>,
32}
33
34pub fn prompt_for_name() -> Result<String, Error> {
35    let valid_ident = regex::Regex::new(r"^([a-zA-Z][a-zA-Z0-9_-]+)$")?;
36    let name = loop {
37        let name = Input::new(&format!(
38            "{} {}",
39            emoji::QUESTION,
40            style("Project Name").bold()
41        ))
42        .interact()?;
43        if valid_ident.is_match(&name) {
44            break name;
45        } else {
46            eprintln!(
47                "{} {} \"{}\" {}",
48                emoji::WARN,
49                style("Sorry,").bold().red(),
50                style(&name).bold().yellow(),
51                style("is not a valid crate name").bold().red()
52            );
53        }
54    };
55    Ok(name)
56}
57
58pub fn create(args: Args) -> Result<(), failure::Error> {
59    let name: &Name = &match &args.name {
60        Some(ref n) => Name::new(n),
61        None => Name::new(&prompt_for_name()?), //Prompt user
62    };
63    let branch = args.branch.unwrap_or_else(|| "master".to_string()); //TODO: Pass in path as arg?
64    let config = GitConfig::new(EE_TEMPLATE.to_string(), branch.clone())?;
65    if let Some(dir) = &create_project_dir(&name) {
66        match git::create(dir, config) {
67            Ok(_) => git::remove_history(dir).unwrap_or(apply_template(name, dir, &branch)?),
68            Err(e) => failure::bail!(
69                "{} {} {}",
70                emoji::ERROR,
71                style("Git Error:").bold().red(),
72                style(e).bold().red(),
73            ),
74        };
75    } else {
76        failure::bail!(
77            "{} {}",
78            emoji::ERROR,
79            style("Error: directory already exists!").bold().red(),
80        );
81    }
82    Ok(())
83}
84
85/// Adapted from:
86/// https://github.com/ashleygwilliams/cargo-generate/blob/5a2b7f988c448ccbda4b2d1c5c619125ccefcfaf/src/lib.rs#L110
87fn create_project_dir(name: &Name) -> Option<PathBuf> {
88    let dir_name = format!("{}",name.kebab_case());  //TODO: Pass in path as arg?
89    let project_dir = env::current_dir()
90        .unwrap_or_else(|_e| ".".into())
91        .join(&dir_name);
92
93    println!(
94        "{} {} `{}`{}",
95        emoji::PICKAXE,
96        style("Creating project ").bold(),
97        style(&dir_name).bold().yellow(),
98        style("...").bold()
99    );
100
101    if project_dir.exists() {
102        None
103    } else {
104        Some(project_dir)
105    }
106}
107
108fn apply_template(name: &Name, dir: &PathBuf, branch: &str) -> Result<(), failure::Error> {
109    let template = template::substitute(name)?;
110    template::walk_dir(dir, template)?;
111    git::init(dir, branch)?;
112    let dir_string = dir.to_str().unwrap_or("");
113    println!(
114        "{} {} {} {}",
115        emoji::UNICORN,
116        style("Boom!").bold().green(),
117        style("New project created").bold(),
118        style(dir_string).underlined()
119    );
120    Ok(())
121}