use crate::{error, ok, get_current_branch, in_repo};
use std::{
env, fs,
fs::File,
io,
io::{Read, Write},
path::Path,
};
pub fn merge(branch: &str) -> io::Result<()> {
if !in_repo() {
return Err(error!("Current directory is not a rvcs repository."));
}
if !branch_exists(branch)? {
return Err(error!("Branch does not exist."));
}
let upcoming_full_path = &format!(".rvcs/commits/{}/", branch);
let upcoming_commits = fs::read_dir(upcoming_full_path)?
.collect::<Vec<_>>()
.into_iter()
.rev()
.map(|e| {
e.unwrap()
.path()
.to_str()
.unwrap()
.to_owned()
.replace(upcoming_full_path, "")
})
.collect::<Vec<_>>();
let current_full_path = &format!(".rvcs/commits/{}/", get_current_branch()?);
let current_commits = fs::read_dir(current_full_path)?
.collect::<Vec<_>>()
.into_iter()
.rev()
.map(|e| {
e.unwrap()
.path()
.to_str()
.unwrap()
.to_owned()
.replace(current_full_path, "")
})
.collect::<Vec<_>>();
let mut upcoming_since_sep = vec![];
let to_iter = if upcoming_commits.len() > current_commits.len() {
current_commits.len()
} else {
upcoming_commits.len()
};
for i in 0..to_iter {
if upcoming_commits[i] != current_commits[i] {
upcoming_since_sep = upcoming_commits[i..].to_vec();
break;
}
}
for commit in upcoming_since_sep {
let upcoming_commit = &format!(".rvcs/commits/{}/{}", branch, commit);
let upcoming_in_current = &format!(".rvcs/commits/{}/{}", get_current_branch()?, commit);
if Path::new(upcoming_commit).is_dir() {
copy_dir(upcoming_commit, upcoming_in_current)?;
} else {
fs::copy(upcoming_commit, upcoming_in_current)?;
}
}
println!("{} Sucessfully merged branch `{}` into `{}`.", ok(), branch, get_current_branch()?);
Ok(())
}
fn readr(dir: &str) -> io::Result<Vec<String>> {
let mut elements = vec![];
for entry in fs::read_dir(dir)? {
let path = entry?.path();
if path.is_dir() {
elements.push(path.to_str().unwrap().to_string());
elements.extend(readr(path.to_str().unwrap())?);
} else {
elements.push(path.to_str().unwrap().to_string());
}
}
Ok(elements)
}
fn copy_dir(from: &str, to: &str) -> io::Result<()> {
let current = env::current_dir()?;
env::set_current_dir(from)?;
let content = readr(".")?;
env::set_current_dir(¤t)?;
fs::create_dir_all(to)?;
env::set_current_dir(to)?;
for element in content {
let stringy = format!("{}/{}/{}", ¤t.to_str().unwrap(), from, &element);
let path = Path::new(&stringy);
if path.is_dir() {
fs::create_dir_all(&element)?;
} else {
let mut buffer = vec![];
File::open(path)?.read_to_end(&mut buffer)?;
File::create(&element)?.write_all(&buffer)?;
}
}
env::set_current_dir(current)
}
pub fn create_branch(name: &str) -> io::Result<()> {
if !in_repo() {
return Err(error!("Current directory is not a rvcs repository."));
}
if branch_exists(name)? {
return Err(error!("Branch already exists."));
}
let current = &format!(".rvcs/commits/{}/", get_current_branch()?);
let to_create = &format!(".rvcs/commits/{}/", name);
copy_dir(¤t, &to_create)?;
println!("{} Sucessfully created branch `{}`.", ok(), name);
Ok(())
}
pub fn goto_branch(name: &str) -> io::Result<()> {
if !in_repo() {
return Err(error!("Current directory is not a rvcs repository."));
}
if !branch_exists(name)? {
return Err(error!("Branch does not exist."));
}
File::create(".rvcs/CURRENT_BRANCH")?.write_all(name.as_bytes())?;
println!("{} Sucessfully switched to branch `{}`.", ok(), name);
Ok(())
}
pub fn delete_branch(name: &str) -> io::Result<()> {
if !in_repo() {
return Err(error!("Current directory is not a rvcs repository."));
}
if !branch_exists(name)? {
return Err(error!("Branch does not exist."));
}
fs::remove_dir_all(&format!(".rvcs/commits/{}", name))?;
println!("{} Sucessfully deleted branch `{}`.", ok(), name);
Ok(())
}
fn branch_exists(name: &str) -> io::Result<bool> {
for entry in fs::read_dir(".rvcs/commits/")? {
let path = entry?.path();
if path.file_name().unwrap().to_str().unwrap() == name {
return Ok(true);
}
}
Ok(false)
}
pub fn list_branches() -> io::Result<()> {
if !in_repo() {
return Err(error!("Current directory is not a rvcs repository."));
}
let current = fs::read_to_string(".rvcs/CURRENT_BRANCH")?;
for entry in fs::read_dir(".rvcs/commits/")? {
let path = entry?.path();
if path.file_name().unwrap().to_str().unwrap() == current {
println!("\x1b[0;32m* {}\x1b[0m", current);
} else {
println!(" {}", path.file_name().unwrap().to_str().unwrap());
}
}
Ok(())
}