bonfida_autoproject/
lib.rs1use 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 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}