use {
super::{err::InitError, git::clone, sep, RepoSummary},
crate::{
init::{
git::use_branch,
input,
rename::rename_fields,
select,
HEADER,
OFFICIAL_REPOSITORY_BRANCHES,
OFFICIAL_REPOSITORY_OPTIONS,
RENDER_CONFIG,
WINI_REPO,
},
utils::{copy_dir_all, generate_random_string},
},
git2::{BranchType, Repository},
inquire::set_global_render_config,
std::{fs, path::Path},
};
pub fn from_official_repository() -> Result<RepoSummary, InitError> {
let handle_clone_official_repository = std::thread::spawn(|| clone(WINI_REPO));
sep();
let result = (|| {
let branch_index = select(
"Which template should be used",
OFFICIAL_REPOSITORY_OPTIONS.to_vec(),
)?;
let branch = OFFICIAL_REPOSITORY_BRANCHES[branch_index].to_owned();
sep();
let project_name = input("Project name:")?;
let path = Path::new(&project_name);
if path.exists() && path.is_dir() {
return Err(InitError::AlreadyExists(project_name));
}
Ok((project_name, branch))
})();
let current_repository_name = handle_clone_official_repository.join().unwrap()?;
match result {
Ok((project_name, branch)) => {
match (|| {
fs::rename(¤t_repository_name, &project_name).map_err(InitError::IoError)?;
let last_commit_hash =
use_branch(&project_name, &branch).map_err(InitError::OtherGitError)?;
Ok(RepoSummary {
dir: project_name,
branch,
last_commit_hash,
remote_url: Some(WINI_REPO.to_string()),
})
})() {
Ok(summary) => Ok(summary),
Err(err) => {
std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
Err(err)
},
}
},
Err(err) => {
std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
Err(err)
},
}
}
pub fn handle_project_setup_for_custom(
current_repository_name: &str,
remote_url: Option<String>,
) -> Result<RepoSummary, InitError> {
let branches = {
let repo = Repository::open(current_repository_name).map_err(InitError::OtherGitError)?;
let branches = repo
.branches(Some(BranchType::Remote))
.map_err(|_| InitError::OtherGitError(git2::Error::from_str("No branch found.")))?;
branches
.filter_map(|e| {
e.ok().and_then(|(b, _)| {
b.name()
.ok()
.flatten()
.map(|name| name.replace("origin/", ""))
})
})
.filter(|s| s != "HEAD")
.collect::<Vec<String>>()
};
let branch_index = select("Which branch should be used ?", branches.clone())?;
let branch = &branches[branch_index];
sep();
let project_name = input("Project name:")?;
let path = Path::new(&project_name);
if path.exists() && path.is_dir() {
return Err(InitError::AlreadyExists(project_name));
}
fs::rename(current_repository_name, &project_name).map_err(InitError::IoError)?;
let last_commit_hash = use_branch(&project_name, branch).map_err(InitError::OtherGitError)?;
Ok(RepoSummary {
dir: project_name,
branch: branch.to_owned(),
last_commit_hash,
remote_url,
})
}
pub fn from_custom_remote_repository() -> Result<RepoSummary, InitError> {
let remote_url = input("Remote repository URL:")?;
let current_repository_name = match clone(&remote_url) {
Ok(n) => n,
Err(InitError::OtherGitError(git_error)) => {
if git_error.code() == git2::ErrorCode::NotFound ||
git_error.class() == git2::ErrorClass::Http
{
eprintln!("{}", InitError::CouldntCloneRepo(remote_url));
sep();
return from_custom_remote_repository();
} else {
return Err(InitError::OtherGitError(git_error));
}
},
Err(fail) => return Err(fail),
};
match handle_project_setup_for_custom(¤t_repository_name, Some(remote_url)) {
Ok(sum) => Ok(sum),
Err(err) => {
std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
Err(err)
},
}
}
pub fn from_cutom_local_repository() -> Result<RepoSummary, InitError> {
let repository_path = {
let mut repository_path: Option<String> = None;
while repository_path.is_none() {
let input_repository_path = input("Local repository path:")?;
let repository_path_struct = Path::new(&input_repository_path);
if repository_path_struct.exists() {
let path_of_git_dir_string = format!("{input_repository_path}/.git");
let path_of_git_dir = Path::new(&path_of_git_dir_string);
if path_of_git_dir.exists() && path_of_git_dir.is_dir() {
repository_path = Some(input_repository_path);
} else {
eprintln!(
"{}",
InitError::PathExistsButIsNotGit(input_repository_path)
)
}
} else {
eprintln!("{}", InitError::InvalidPath(input_repository_path))
}
}
repository_path.expect("Can't be None.")
};
let current_repository_name = generate_random_string(64);
copy_dir_all(repository_path, ¤t_repository_name).map_err(InitError::IoError)?;
match handle_project_setup_for_custom(¤t_repository_name, None) {
Ok(sum) => Ok(sum),
Err(err) => {
std::fs::remove_dir_all(current_repository_name).map_err(InitError::IoError)?;
Err(err)
},
}
}
pub fn ask() -> Result<(), InitError> {
set_global_render_config(*RENDER_CONFIG);
println!("{HEADER}");
sep();
let selection = select(
"Create a project from",
vec![
"Official wini templates",
"Remote git repository",
"Local git repository",
],
)?;
sep();
let repo_summary = match selection {
0 => from_official_repository()?,
1 => from_custom_remote_repository()?,
2 => from_cutom_local_repository()?,
_ => unreachable!(),
};
rename_fields(&repo_summary)?;
sep();
println!(
"\x1B[32m◆\x1B[0m Project created at `\x1B[32;1m./{}\x1b[0m`!",
repo_summary.dir
);
Ok(())
}