1use crate::{error, ok, get_current_branch, in_repo};
2use std::{
3 env, fs,
4 fs::File,
5 io,
6 io::{Read, Write},
7 path::Path,
8};
9
10pub fn merge(branch: &str) -> io::Result<()> {
11 if !in_repo() {
12 return Err(error!("Current directory is not a rvcs repository."));
13 }
14
15 if !branch_exists(branch)? {
16 return Err(error!("Branch does not exist."));
17 }
18
19 let upcoming_full_path = &format!(".rvcs/commits/{}/", branch);
20 let upcoming_commits = fs::read_dir(upcoming_full_path)?
21 .collect::<Vec<_>>()
22 .into_iter()
23 .rev()
24 .map(|e| {
25 e.unwrap() .path()
27 .to_str()
28 .unwrap()
29 .to_owned()
30 .replace(upcoming_full_path, "")
31 })
32 .collect::<Vec<_>>();
33
34 let current_full_path = &format!(".rvcs/commits/{}/", get_current_branch()?);
35 let current_commits = fs::read_dir(current_full_path)?
36 .collect::<Vec<_>>()
37 .into_iter()
38 .rev()
39 .map(|e| {
40 e.unwrap() .path()
42 .to_str()
43 .unwrap()
44 .to_owned()
45 .replace(current_full_path, "")
46 })
47 .collect::<Vec<_>>();
48
49 let mut upcoming_since_sep = vec![];
50
51 let to_iter = if upcoming_commits.len() > current_commits.len() {
52 current_commits.len()
53 } else {
54 upcoming_commits.len()
55 };
56
57 for i in 0..to_iter {
58 if upcoming_commits[i] != current_commits[i] {
59 upcoming_since_sep = upcoming_commits[i..].to_vec();
60 break;
61 }
62 }
63
64 for commit in upcoming_since_sep {
65 let upcoming_commit = &format!(".rvcs/commits/{}/{}", branch, commit);
66 let upcoming_in_current = &format!(".rvcs/commits/{}/{}", get_current_branch()?, commit);
67
68 if Path::new(upcoming_commit).is_dir() {
69 copy_dir(upcoming_commit, upcoming_in_current)?;
70 } else {
71 fs::copy(upcoming_commit, upcoming_in_current)?;
72 }
73 }
74
75 println!("{} Sucessfully merged branch `{}` into `{}`.", ok(), branch, get_current_branch()?);
76
77 Ok(())
78}
79
80fn readr(dir: &str) -> io::Result<Vec<String>> {
81 let mut elements = vec![];
82
83 for entry in fs::read_dir(dir)? {
84 let path = entry?.path();
85
86 if path.is_dir() {
87 elements.push(path.to_str().unwrap().to_string());
88 elements.extend(readr(path.to_str().unwrap())?);
89 } else {
90 elements.push(path.to_str().unwrap().to_string());
91 }
92 }
93 Ok(elements)
94}
95
96fn copy_dir(from: &str, to: &str) -> io::Result<()> {
97 let current = env::current_dir()?;
98
99 env::set_current_dir(from)?;
100 let content = readr(".")?;
101
102 env::set_current_dir(¤t)?;
103 fs::create_dir_all(to)?;
104 env::set_current_dir(to)?;
105
106 for element in content {
107 let stringy = format!("{}/{}/{}", ¤t.to_str().unwrap(), from, &element);
108 let path = Path::new(&stringy);
109 if path.is_dir() {
110 fs::create_dir_all(&element)?;
111 } else {
112 let mut buffer = vec![];
113 File::open(path)?.read_to_end(&mut buffer)?;
114 File::create(&element)?.write_all(&buffer)?;
115 }
116 }
117
118 env::set_current_dir(current)
119}
120
121pub fn create_branch(name: &str) -> io::Result<()> {
122 if !in_repo() {
123 return Err(error!("Current directory is not a rvcs repository."));
124 }
125
126 if branch_exists(name)? {
127 return Err(error!("Branch already exists."));
128 }
129 let current = &format!(".rvcs/commits/{}/", get_current_branch()?);
130 let to_create = &format!(".rvcs/commits/{}/", name);
131
132 copy_dir(¤t, &to_create)?;
133 println!("{} Sucessfully created branch `{}`.", ok(), name);
134 Ok(())
135}
136
137pub fn goto_branch(name: &str) -> io::Result<()> {
138 if !in_repo() {
139 return Err(error!("Current directory is not a rvcs repository."));
140 }
141
142 if !branch_exists(name)? {
143 return Err(error!("Branch does not exist."));
144 }
145 File::create(".rvcs/CURRENT_BRANCH")?.write_all(name.as_bytes())?;
146 println!("{} Sucessfully switched to branch `{}`.", ok(), name);
147 Ok(())
148}
149
150pub fn delete_branch(name: &str) -> io::Result<()> {
151 if !in_repo() {
152 return Err(error!("Current directory is not a rvcs repository."));
153 }
154
155 if !branch_exists(name)? {
156 return Err(error!("Branch does not exist."));
157 }
158
159 fs::remove_dir_all(&format!(".rvcs/commits/{}", name))?;
160
161 println!("{} Sucessfully deleted branch `{}`.", ok(), name);
162
163 Ok(())
164}
165
166fn branch_exists(name: &str) -> io::Result<bool> {
167 for entry in fs::read_dir(".rvcs/commits/")? {
168 let path = entry?.path();
169
170 if path.file_name().unwrap().to_str().unwrap() == name {
171 return Ok(true);
172 }
173 }
174 Ok(false)
175}
176
177pub fn list_branches() -> io::Result<()> {
178 if !in_repo() {
179 return Err(error!("Current directory is not a rvcs repository."));
180 }
181
182 let current = fs::read_to_string(".rvcs/CURRENT_BRANCH")?;
183
184 for entry in fs::read_dir(".rvcs/commits/")? {
185 let path = entry?.path();
186
187 if path.file_name().unwrap().to_str().unwrap() == current {
188 println!("\x1b[0;32m* {}\x1b[0m", current);
189 } else {
190 println!(" {}", path.file_name().unwrap().to_str().unwrap());
191 }
192 }
193 Ok(())
194}