1use {
2 super::{err::InitError, git::clone, sep, RepoSummary},
3 crate::{
4 init::{
5 git::use_branch,
6 input,
7 rename::rename_fields,
8 select,
9 HEADER,
10 OFFICIAL_REPOSITORY_BRANCHES,
11 OFFICIAL_REPOSITORY_OPTIONS,
12 RENDER_CONFIG,
13 WINI_REPO,
14 },
15 utils::{copy_dir_all, generate_random_string},
16 },
17 git2::{BranchType, Repository},
18 inquire::set_global_render_config,
19 std::{fs, path::Path},
20};
21
22
23
24pub fn from_official_repository() -> Result<RepoSummary, InitError> {
26 let handle_clone_official_repository = std::thread::spawn(|| clone(WINI_REPO));
27
28 sep();
29
30 let result = (|| {
31 let branch_index = select(
32 "Which template should be used",
33 OFFICIAL_REPOSITORY_OPTIONS.to_vec(),
34 )?;
35 let branch = OFFICIAL_REPOSITORY_BRANCHES[branch_index].to_owned();
36
37 sep();
38
39 let project_name = input("Project name:")?;
40
41 let path = Path::new(&project_name);
42 if path.exists() && path.is_dir() {
43 return Err(InitError::AlreadyExists(project_name));
44 }
45
46 Ok((project_name, branch))
47 })();
48
49 let current_repository_name = handle_clone_official_repository.join().unwrap()?;
54
55 match result {
56 Ok((project_name, branch)) => {
57 match (|| {
60 fs::rename(¤t_repository_name, &project_name).map_err(InitError::IoError)?;
61
62 let last_commit_hash =
63 use_branch(&project_name, &branch).map_err(InitError::OtherGitError)?;
64
65 Ok(RepoSummary {
66 dir: project_name,
67 branch,
68 last_commit_hash,
69 remote_url: Some(WINI_REPO.to_string()),
70 })
71 })() {
72 Ok(summary) => Ok(summary),
73 Err(err) => {
74 std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
75 Err(err)
76 },
77 }
78 },
79 Err(err) => {
80 std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
81 Err(err)
82 },
83 }
84}
85
86
87
88pub fn handle_project_setup_for_custom(
91 current_repository_name: &str,
92 remote_url: Option<String>,
93) -> Result<RepoSummary, InitError> {
94 let branches = {
95 let repo = Repository::open(current_repository_name).map_err(InitError::OtherGitError)?;
96 let branches = repo
97 .branches(Some(BranchType::Remote))
98 .map_err(|_| InitError::OtherGitError(git2::Error::from_str("No branch found.")))?;
99
100 branches
101 .filter_map(|e| {
102 e.ok().and_then(|(b, _)| {
103 b.name()
104 .ok()
105 .flatten()
106 .map(|name| name.replace("origin/", ""))
107 })
108 })
109 .filter(|s| s != "HEAD")
110 .collect::<Vec<String>>()
111 };
112 let branch_index = select("Which branch should be used ?", branches.clone())?;
113 let branch = &branches[branch_index];
114
115 sep();
116
117 let project_name = input("Project name:")?;
118
119 let path = Path::new(&project_name);
120 if path.exists() && path.is_dir() {
121 return Err(InitError::AlreadyExists(project_name));
122 }
123
124 fs::rename(current_repository_name, &project_name).map_err(InitError::IoError)?;
125
126 let last_commit_hash = use_branch(&project_name, branch).map_err(InitError::OtherGitError)?;
127
128 Ok(RepoSummary {
129 dir: project_name,
130 branch: branch.to_owned(),
131 last_commit_hash,
132 remote_url,
133 })
134}
135
136
137pub fn from_custom_remote_repository() -> Result<RepoSummary, InitError> {
139 let remote_url = input("Remote repository URL:")?;
140
141 let current_repository_name = match clone(&remote_url) {
142 Ok(n) => n,
143 Err(InitError::OtherGitError(git_error)) => {
144 if git_error.code() == git2::ErrorCode::NotFound ||
145 git_error.class() == git2::ErrorClass::Http
146 {
147 eprintln!("{}", InitError::CouldntCloneRepo(remote_url));
148 sep();
149
150 return from_custom_remote_repository();
151 } else {
152 return Err(InitError::OtherGitError(git_error));
153 }
154 },
155 Err(fail) => return Err(fail),
156 };
157
158 match handle_project_setup_for_custom(¤t_repository_name, Some(remote_url)) {
159 Ok(sum) => Ok(sum),
160 Err(err) => {
161 std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
162 Err(err)
163 },
164 }
165}
166
167
168
169pub fn from_cutom_local_repository() -> Result<RepoSummary, InitError> {
171 let repository_path = {
173 let mut repository_path: Option<String> = None;
174
175 while repository_path.is_none() {
176 let input_repository_path = input("Local repository path:")?;
177
178 let repository_path_struct = Path::new(&input_repository_path);
179
180 if repository_path_struct.exists() {
181 let path_of_git_dir_string = format!("{input_repository_path}/.git");
182 let path_of_git_dir = Path::new(&path_of_git_dir_string);
183
184 if path_of_git_dir.exists() && path_of_git_dir.is_dir() {
185 repository_path = Some(input_repository_path);
186 } else {
187 eprintln!(
188 "{}",
189 InitError::PathExistsButIsNotGit(input_repository_path)
190 )
191 }
192 } else {
193 eprintln!("{}", InitError::InvalidPath(input_repository_path))
194 }
195 }
196
197 repository_path.expect("Can't be None.")
198 };
199
200 let current_repository_name = generate_random_string(64);
201 copy_dir_all(repository_path, ¤t_repository_name).map_err(InitError::IoError)?;
202
203
204 match handle_project_setup_for_custom(¤t_repository_name, None) {
205 Ok(sum) => Ok(sum),
206 Err(err) => {
207 std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
208 Err(err)
209 },
210 }
211}
212
213
214pub fn ask() -> Result<(), InitError> {
216 set_global_render_config(*RENDER_CONFIG);
217
218 println!("{HEADER}");
219
220 sep();
221
222 let selection = select(
223 "Create a project from",
224 vec![
225 "Official wini templates",
226 "Remote git repository",
227 "Local git repository",
228 ],
229 )?;
230
231 sep();
232
233 let repo_summary = match selection {
234 0 => from_official_repository()?,
235 1 => from_custom_remote_repository()?,
236 2 => from_cutom_local_repository()?,
237 _ => unreachable!(),
238 };
239
240 rename_fields(&repo_summary)?;
241
242 sep();
243 println!(
244 "\x1B[32m◆\x1B[0m Project created at `\x1B[32;1m./{}\x1b[0m`!",
245 repo_summary.dir
246 );
247
248 Ok(())
249}