1#![allow(clippy::too_many_arguments)]
5#![allow(clippy::cognitive_complexity)]
6
7extern crate case;
8extern crate clap;
9extern crate colored;
10extern crate git2;
11extern crate heck;
12extern crate rustache;
13#[macro_use]
14extern crate serde_derive;
15extern crate tempdir;
16extern crate time;
17extern crate toml;
18
19use case::*;
20use colored::*;
21use heck::*;
22use rustache::{HashBuilder, VecBuilder};
23use std::fs;
24use std::fs::File;
25use std::io::prelude::*;
26use std::path::Path;
27use std::path::PathBuf;
28use toml::Value::Table;
29
30pub mod includes;
31pub mod render;
32pub mod repo;
33pub mod types;
34
35pub fn read_toml_dir(template_path: &str, home: PathBuf) -> (types::Project, bool) {
40 let (mut template_file, is_global_template) = if let Ok(f) = File::open(&template_path) {
41 (f, false)
42 } else if let Ok(f) = {
43 let mut p = home;
44 p.push(".pi_templates/");
45 p.push(template_path);
46 File::open(p)
47 } {
48 (f, true)
49 } else {
50 println!(
51 "{}: File {:?} could not be opened. Check that it exists.",
52 "Error".red(),
53 template_path
54 );
55 std::process::exit(0x0f00);
56 };
57 let mut template = String::new();
58 template_file
59 .read_to_string(&mut template)
60 .expect("Failed to read file"); (read_toml_str(&template, template_path), is_global_template)
62}
63
64pub fn read_toml_str(template: &str, template_path: &str) -> types::Project {
66 let extract = toml::from_str(template);
67 if let Ok(t) = extract {
68 t
69 } else if let Err(e) = extract {
70 println!("Error parsing {:?}: {}", template_path, e);
71 std::process::exit(0x0f00);
72 } else {
73 std::process::exit(0x0f00);
74 }
75}
76
77pub fn read_toml_config(config_path: &std::path::PathBuf) -> types::Config {
79 let file = if let Ok(f) = File::open(&config_path) {
80 Some(f)
81 } else {
82 println!(
83 "{}: File {:?} could not be opened. Check that it exists.",
84 "Warning".yellow(),
85 config_path
86 );
87 None
88 };
89 let mut toml_str = String::new();
90 let maybe_file = file.map(|mut x| x.read_to_string(&mut toml_str));
91 let extract = toml::from_str(&toml_str);
92 if maybe_file.is_some() && maybe_file.unwrap().is_ok() {
93 if let Ok(t) = extract {
94 t
95 } else if let Err(e) = extract {
96 println!("Error parsing {:?}: {}", config_path, e);
97 std::process::exit(0x0f00);
98 } else {
99 std::process::exit(0x0f00);
100 }
101 } else {
102 eprintln!(
103 "{}: No ~/.pi.toml found. Using defaults.",
104 "Warning".yellow()
105 );
106 types::Config {
107 version_control: None,
108 author: None,
109 license: None,
110 user: None,
111 }
112 }
113}
114
115pub fn init_helper(
116 home: PathBuf,
117 project_dir: &str,
118 decoded: types::Config,
119 author: types::Author,
120 name: &str,
121 year: i32,
122 current_date: &str,
123 force: bool,
124 parsed_toml: types::Project,
125 is_global_project: bool,
126) {
127 let project = if is_global_project {
128 let mut p = home;
129 p.push(".pi_templates/");
130 p.push(project_dir);
131 p.to_str().unwrap().to_string()
132 } else {
133 project_dir.to_string()
134 };
135 let parsed_dirs = parsed_toml.files;
136 let parsed_config = parsed_toml.config;
137
138 let (license_contents, license_name) =
140 if let Some(l) = parsed_toml.license {
142 match l.as_str() {
143 "BSD3" => (Some(includes::BSD3), "BSD3"),
144 "BSD" => (Some(includes::BSD), "BSD"),
145 "MIT" => (Some(includes::MIT), "MIT"),
146 "GPL3" => (Some(includes::GPL3), "GLP3"),
147 "AllRightsReserved" => (Some(includes::BSD3), "AllRightsReserved"),
148 _ => { println!("{}: requested license not found. Defaulting to AllRightsReserved","Warning".yellow())
149 ; (Some(includes::ALL_RIGHTS_RESERVED), "AllRightsReserved") }
150 }
151 }
152 else if let Some(l) = decoded.license {
153 match l.as_str() {
154 "BSD3" => (Some(includes::BSD3), "BSD3"),
155 "BSD" => (Some(includes::BSD), "BSD"),
156 "MIT" => (Some(includes::MIT), "MIT"),
157 "GPL3" => (Some(includes::GPL3), "GLP3"),
158 "AllRightsReserved" => (Some(includes::BSD3), "AllRightsReserved"),
159 _ => { println!("{}: requested license not found. Defaulting to AllRightsReserved","Warning".yellow())
160 ; (Some(includes::ALL_RIGHTS_RESERVED), "AllRightsReserved") }
161 }
162 }
163 else {
164 (None,"")
165 };
166
167 let version = if let Some(config) = parsed_config.clone() {
169 if let Some(v) = config.version {
170 v
171 } else {
172 "0.1.0".to_string()
173 }
174 } else {
175 eprintln!(
176 "{}: no version info found, defaulting to '0.1.0'",
177 "Warning".yellow()
178 );
179 "0.1.0".to_string()
180 };
181
182 let github_username = if let Some(uname) = author.github_username {
184 uname
185 } else {
186 eprintln!(
187 "{}: no github username found, defaulting to null",
188 "Warning".yellow()
189 );
190 "".to_string()
191 };
192
193 let user_keys = if let Some(u) = parsed_toml.user {
195 match u.toml {
196 Table(t) => Some(t),
197 _ => None,
198 }
199 } else {
200 None
201 };
202
203 let user_keys_global = if let Some(u) = decoded.user {
205 match u.toml {
206 Table(t) => Some(t),
207 _ => None,
208 }
209 } else {
210 None
211 };
212
213 let mut hash = HashBuilder::new();
215 if let Some(x) = user_keys {
217 for (key, value) in &x {
218 if let Some(a) = value.as_str() {
219 hash = hash.insert(key, a);
220 }
221 }
222 }
223 if let Some(x) = user_keys_global {
225 for (key, value) in &x {
226 if let Some(a) = value.as_str() {
227 hash = hash.insert(key, a);
228 }
229 }
230 }
231 hash = hash
233 .insert("project", name)
234 .insert("Project", name.to_capitalized())
235 .insert("ProjectCamelCase", name.to_camel_case())
236 .insert("year", year)
237 .insert("name", author.name)
238 .insert("version", version)
239 .insert("email", author.email)
240 .insert("github_username", github_username)
241 .insert("license", license_name)
242 .insert("date", current_date);
243
244 if Path::new(name).exists() && !force {
246 println!(
247 "Path '{}' already exists. Rerun with -f or --force to overwrite.",
248 name
249 );
250 std::process::exit(0x0f00);
251 };
252
253 let _ = fs::create_dir(name);
255 if let Some(dirs_pre) = parsed_dirs.directories {
256 render::render_dirs(dirs_pre, &hash, name);
257 }
258
259 let files = if let Some(files_pre) = parsed_dirs.files {
262 render::render_files(files_pre, &hash, name) } else {
264 VecBuilder::new()
265 };
266
267 if let Some(lic) = license_contents {
269 render::render_file(lic, name, "LICENSE", &hash);
270 }
271
272 if let Some(readme) = parsed_toml.with_readme {
274 if readme {
275 render::render_file(includes::README, name, "README.md", &hash);
276 }
277 }
278
279 hash = hash.insert("files", files);
281
282 render::render_templates(&project, name, &hash, parsed_dirs.templates, false);
284
285 render::render_templates(&project, name, &hash, parsed_dirs.scripts, true);
287
288 if let Some(config) = parsed_config {
290 if let Some(vc) = config.version_control {
291 match vc.as_str() {
292 "git" => repo::git_init(name),
293 "hg" | "mercurial" => repo::hg_init(name),
294 "pijul" => repo::pijul_init(name),
295 "darcs" => repo::darcs_init(name),
296 _ => {
297 eprintln!(
298 "{}: version control {} is not yet supported. Supported version control tools are darcs, pijul, mercurial, and git.",
299 "Error".red(),
300 vc
301 );
302 }
303 }
304 }
305 } else if let Some(vc) = decoded.version_control {
306 match vc.as_str() {
307 "git" => repo::git_init(name),
308 "hg" | "mercurial" => repo::hg_init(name),
309 "pijul" => repo::pijul_init(name),
310 "darcs" => repo::darcs_init(name),
311 _ => {
312 eprintln!(
313 "{}: version control {} is not yet supported. Supported version control tools are darcs, pijul, mercurial, and git.",
314 "Error".red(),
315 vc
316 );
317 }
318 }
319 }
320
321 println!("Finished initializing project in {}/", name);
323}