bonfida_autoproject/
lib.rs

1use clap::{crate_name, crate_version, Arg, ArgMatches, Command};
2use convert_case::{Case, Casing};
3use fs_extra::dir::get_dir_content;
4use include_dir::{include_dir, Dir};
5use path_absolutize::Absolutize;
6
7use std::{
8    ffi::OsStr,
9    fs::{self, OpenOptions},
10    io::Write,
11    path::PathBuf,
12    str::FromStr,
13    time::Instant,
14};
15
16const CASE_STR_ID: [&str; 4] = [
17    "TOBEREPLACEDBY_UPPERSNAKE",
18    "TOBEREPLACEDBY_LOWERSNAKE",
19    "TOBEREPLACEDBY_KEBAB",
20    "TOBEREPLACEDBY_PASCAL",
21];
22
23const TEMPLATE: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src/template");
24
25pub fn command() -> Command<'static> {
26    Command::new(crate_name!())
27        .version(crate_version!())
28        .author("Bonfida")
29        .about("Initialize a new project")
30        .arg(
31            Arg::new("name")
32                .required(true)
33                .takes_value(true)
34                .help("The new project's name"),
35        )
36        .arg(
37            Arg::new("new-project-path")
38                .long("path")
39                .short('p')
40                .takes_value(true)
41                .required(false)
42                .help("Path in which to create the new project. Default to current directory."),
43        )
44}
45
46pub fn process(matches: &ArgMatches) {
47    let project_name = matches.value_of("name").unwrap();
48    let project_path = matches.value_of("new-project-path").unwrap_or(".");
49
50    if !project_name.is_case(Case::Kebab) {
51        println!("Project name should be given in-kebab-case.");
52        panic!();
53    };
54
55    generate(project_name, project_path);
56}
57
58pub fn generate(project_name: &str, project_path: &str) {
59    let now = Instant::now();
60
61    let mut project_dir = std::path::PathBuf::from_str(project_path).unwrap();
62    project_dir.push(project_name);
63
64    let project_dir = project_dir.absolutize().unwrap();
65
66    println!("{:?}", project_dir);
67
68    // return;
69
70    fs::create_dir_all(&project_dir).unwrap();
71
72    TEMPLATE.extract(&project_dir).unwrap();
73
74    let directory = get_dir_content(project_dir).unwrap().files;
75
76    for file_path_str in directory {
77        let mut file_path = PathBuf::from(file_path_str);
78        eprintln!("{file_path:?}");
79        if file_path.file_name() == Some(OsStr::new("_Cargo.toml")) {
80            let mut new_file_path = file_path.to_owned();
81            new_file_path.set_file_name("Cargo.toml");
82            std::fs::rename(file_path, &new_file_path).unwrap();
83            file_path = new_file_path;
84        }
85        let mut raw_file = std::fs::read_to_string(&file_path).unwrap();
86
87        for case_id_str in CASE_STR_ID.iter() {
88            raw_file = raw_file.replace(
89                case_id_str,
90                &project_name
91                    .from_case(Case::Kebab)
92                    .to_case(get_case_from_id(case_id_str)),
93            );
94        }
95
96        let mut out_file = OpenOptions::new()
97            .write(true)
98            .truncate(true)
99            .open(file_path)
100            .unwrap();
101        out_file.write_all(raw_file.as_bytes()).unwrap();
102    }
103
104    let elapsed = now.elapsed();
105    println!("✨  Done in {:.2?}", elapsed);
106}
107
108fn get_case_from_id(id_str: &str) -> Case {
109    match id_str {
110        "TOBEREPLACEDBY_UPPERSNAKE" => Case::UpperSnake,
111        "TOBEREPLACEDBY_LOWERSNAKE" => Case::Snake,
112        "TOBEREPLACEDBY_KEBAB" => Case::Kebab,
113        "TOBEREPLACEDBY_PASCAL" => Case::Pascal,
114        _ => panic!(),
115    }
116}