1pub mod rel;
2pub mod config;
3pub mod history;
4pub mod hook;
5pub mod meta;
6
7use crate::checker::Checker;
8use crate::models::{LoopBranch, ListType};
9use crate::utils;
10use std::collections::HashSet;
11use config::Config;
12use rel::Relations;
13use history::History;
14use meta::Meta;
15use crate::RifError;
16use std::path::{Path, PathBuf};
17use crate::consts::*;
18
19pub struct Rif {
21 config: Config,
22 history: History,
23 relation: Relations,
24 meta: Meta,
25 black_list: HashSet<PathBuf>,
26 root_path: Option<PathBuf>,
27}
28
29impl Rif {
30 pub fn new(path: Option<impl AsRef<Path>>) -> Result<Self, RifError> {
32 let config = Config::read_from_file(path.as_ref())?;
33 let black_list = utils::get_black_list(config.git_ignore)?;
34 Ok(Self {
35 config,
36 history: History::read_from_file(path.as_ref())?,
37 relation: Relations::read_from_file(path.as_ref())?,
38 meta: Meta::read_from_file(path.as_ref())?,
39 black_list,
40 root_path: path.map(|p| p.as_ref().clone().to_owned()),
41 })
42 }
43
44 pub fn init(path: Option<impl AsRef<Path>>,create_rif_ignore: bool) -> Result<(), RifError> {
51 let path = if let Some(path) = path {
52 path.as_ref().to_owned()
53 } else { std::env::current_dir()? };
54
55 if path.join(RIF_DIECTORY).exists() {
57 return Err(RifError::RifIoError(String::from("Directory is already initiated")));
58 }
59
60 std::fs::create_dir(path.join(RIF_DIECTORY))?;
62
63 let new_relations = Relations::new();
65 new_relations.save_to_file(Some(&path))?;
66 let new_rif_history = History::new();
68 new_rif_history.save_to_file(Some(&path))?;
69 let new_config = Config::new();
71 new_config.save_to_file(Some(&path))?;
72 let new_meta = Meta::new();
74 new_meta.save_to_file(Some(&path))?;
75 println!("Initiated a rif directory \"{}\"", std::env::current_dir()?.display());
76
77 if create_rif_ignore {
79 std::fs::write(".rifignore",".git")?;
80 }
81 Ok(())
82 }
83
84 pub fn add(&mut self, files: &Vec<impl AsRef<Path>>, force: bool) -> Result<(), RifError> {
89 for file in files {
90 let mut path = file.as_ref().to_owned();
91
92 if !path.exists() {
94 continue;
95 }
96
97 if path.to_str().unwrap() == "." {
100 path = std::env::current_dir()?;
101 self.add_directory(&path)?;
102 continue;
103 } else if path.is_dir() {
104 self.add_directory(&path)?;
105 continue;
106 }
107
108 if self.is_in_black_list(&path) {
110 continue;
111 }
112
113 if self.relation.files.contains_key(&path) {
116 self.add_old_file(&path, force)?;
117 } else {
118 self.add_new_file(&path)?;
119 }
120 } self.relation.save_to_file(self.root_path.as_ref())?;
124 self.meta.save_to_file(self.root_path.as_ref())?;
125 Ok(())
126 }
127
128 pub fn revert(&mut self, files: Option<&Vec<impl AsRef<Path>>>) -> Result<(), RifError> {
132 if let Some(files) = files {
133 for file in files {
134 let path = file.as_ref();
135 self.meta.remove_add_queue(&path);
137 } } else {
139 self.meta.clear();
141 }
142
143 self.meta.save_to_file(self.root_path.as_ref())?;
144 Ok(())
145 }
146
147 pub fn commit(&mut self, message: Option<&str>) -> Result<(), RifError> {
151
152 if self.relation.get_deleted_files().len() != self.meta.to_be_deleted.len() {
154 return Err(RifError::CommitFail("Commit without deleted files are illegal. Rejected".to_owned()))
155 }
156
157 for file in self.meta.to_be_deleted.clone().iter() {
159 self.remove_file(file)?;
160 }
161
162 for file in self.meta.to_be_registerd.clone().into_iter() {
164 self.register_new_file(&file, message)?;
165 }
166
167 for file in self.meta.to_be_forced.iter() {
169 self.relation.update_filestamp_force(&file)?;
170 }
171
172 for file in self.meta.to_be_added.iter() {
174 self.relation.update_filestamp(&file)?;
175
176 if let Some(msg) = message {
178 self.history.add_history(&file, msg)?;
179 self.history.save_to_file(self.root_path.as_ref())?;
180 }
181 }
182
183 if self.meta.to_be_added_later().count() != 0 {
185 self.check_exec()?;
186 }
187
188 self.meta.clear();
190
191 self.meta.save_to_file(self.root_path.as_ref())?;
193 self.relation.save_to_file(self.root_path.as_ref())?;
194 self.history.save_to_file(self.root_path.as_ref())?;
195
196 Ok(())
197 }
198
199 pub fn discard(&mut self, file: impl AsRef<Path>) -> Result<(), RifError> {
203 self.relation.discard_change(file.as_ref())?;
204 self.relation.save_to_file(self.root_path.as_ref())?;
205 Ok(())
206 }
207
208 pub fn rename(&mut self, source_name: &str, new_name: &str) -> Result<(), RifError> {
216 let source_name = Path::new(source_name);
217 let new_name = Path::new(new_name);
218 if let Some(_) = self.relation.files.get(new_name) {
219 return Err(RifError::RenameFail(format!("Rename target: \"{}\" already exists", new_name.display())));
220 }
221
222 if source_name.exists() && self.relation.files.contains_key(source_name) {
224 if !new_name.exists() {
225 std::fs::rename(source_name, new_name)?;
226 } else {
227 return Err(RifError::RenameFail("New name already exists".to_owned()));
228 }
229 }
230
231 self.relation.rename_file(source_name, new_name)?;
232 self.relation.save_to_file(self.root_path.as_ref())?;
233 Ok(())
234 }
235
236 pub fn remove(&mut self, files: &Vec<impl AsRef<Path>>) -> Result<(), RifError> {
238 for file in files {
239 self.remove_file(file.as_ref())?;
240 }
241 self.relation.save_to_file(self.root_path.as_ref())?;
242 self.history.save_to_file(self.root_path.as_ref())?;
243 Ok(())
244 }
245
246 pub fn set(&mut self, file: &Path, refs : &Vec<impl AsRef<Path>>) -> Result<(), RifError> {
248 let refs: HashSet<PathBuf> = refs.iter().map(|a| a.as_ref().to_owned()).collect();
249
250 self.relation.add_reference(file, &refs)?;
251 self.relation.save_to_file(self.root_path.as_ref())?;
252 Ok(())
253 }
254
255 pub fn unset(&mut self, file: &Path, refs : &Vec<impl AsRef<Path>>) -> Result<(), RifError> {
257 let refs: HashSet<PathBuf> = refs.iter().map(|a| a.as_ref().to_owned()).collect();
258
259 self.relation.remove_reference(file, &refs)?;
260 self.relation.save_to_file(self.root_path.as_ref())?;
261 Ok(())
262 }
263
264 pub fn status(&mut self, ignore: bool, verbose: bool) -> Result<(), RifError> {
266 self.meta.remove_non_exsitent();
268
269 let mut to_be_added_later = self.meta.to_be_added_later().peekable();
270
271 if let Some(_) = to_be_added_later.peek() {
272 println!("# Changes to be commited :");
273 for item in &self.meta.to_be_registerd {
274 let format = format!(" new file : {}", &item.to_str().unwrap());
275 println!("{}", utils::green(&format));
276 }
277 for item in &self.meta.to_be_added {
278 let format = format!(" modified : {}", &item.to_str().unwrap());
279 println!("{}", utils::green(&format));
280 }
281 for item in &self.meta.to_be_forced {
282 let format = format!(" forced : {}", &item.to_str().unwrap());
283 println!("{}", utils::green(&format));
284 }
285 for item in &self.meta.to_be_deleted {
286 let format = format!(" deleted : {}", &item.to_str().unwrap());
287 println!("{}", utils::green(&format));
288 }
289 println!("");
290 }
291
292 println!("# Changed files :");
293 self.relation.track_modified_files(self.meta.to_be_added_later())?;
294
295 if !ignore {
297 println!("\n# Untracked files :");
300 self.relation.track_unregistered_files(&self.black_list, &self.meta.to_be_registerd)?;
301 }
302
303 if verbose {
304 println!("\n# Current rif status:\n---");
305 print!("{}", self.relation);
306 }
307
308 self.meta.save_to_file(self.root_path.as_ref())?;
310
311 Ok(())
312 }
313
314 pub fn list(&self, file : Option<impl AsRef<Path>>, list_type: ListType, depth: Option<usize>) -> Result<(), RifError> {
316 if let Some(file) = file {
317 self.relation.display_file_depth(file.as_ref(), 0)?;
319
320 println!("\n# History : ");
322 self.history.print_history(file.as_ref())?;
323 } else { match list_type {
325 ListType::All => {
326 self.relation.display_depth(depth.unwrap_or(0))?;
327 }
328 ListType::Stale => {
329 self.relation.display_stale_files(depth.unwrap_or(0))?;
330 }
331 _ => (),
333 }
334 }
335 Ok(())
336 }
337
338 pub fn data(&self, data_type: Option<&str>, compact: bool) -> Result<(), RifError> {
340 if let Some(data_type) = data_type {
341 match data_type {
342 "meta" => {
343 println!("{:#?}", self.meta);
344 }
345 "history" => {
346 println!("{:#?}", self.history);
347 }
348 _ => () }
350 } else {
351 if compact {
352 println!("{:?}", self.relation);
353 } else {
354 println!("{:#?}", self.relation);
355 }
356 }
357
358 Ok(())
359 }
360
361 pub fn depend(&self, file: &Path) -> Result<(), RifError> {
363 let dependes = self.relation.find_depends(file)?;
364 println!("Files that depends on \"{}\"", file.display());
365 println!("=====");
366 for item in dependes {
367 println!("{}", utils::green(&item.display().to_string()));
368 }
369 Ok(())
370 }
371
372 pub fn check(&mut self) -> Result<(), RifError> {
374 if self.relation.get_deleted_files().len() != 0 {
375 return Err(RifError::CheckerError("Check with deleted files are illegal. Rejected".to_owned()));
376 }
377
378 self.check_exec()?;
379 Ok(())
380 }
381
382 pub fn sanity(&mut self, fix: bool) -> Result<(), RifError> {
384 if fix {
388 self.relation.sanity_fix()?;
389 self.relation.save_to_file(self.root_path.as_ref())?;
390 println!("Sucessfully fixed the rif file");
391 } else {
392 self.relation.sanity_check()?;
393 println!("Sucessfully checked the rif file");
394 }
395 Ok(())
396 }
397
398 fn check_exec(&mut self) -> Result<(), RifError> {
405 let mut checker = Checker::with_relations(&self.relation)?;
407 let changed_files = checker.check(&mut self.relation)?;
408
409 if changed_files.len() != 0 && self.config.hook.trigger {
410 println!("\nHook Output");
411 self.config.hook.execute(changed_files)?;
412 }
413
414 Ok(())
415 }
416
417 fn is_in_black_list(&self, path: &Path) -> bool {
419 if let Some(_) = self.black_list.get(path) {
421 if BLACK_LIST.to_vec().contains(&path.to_str().unwrap()) {
425 eprintln!("File : \"{}\" is not allowed", path.display());
426 } else {
427 println!("\"{}\" is in rifignore file, which is ignored.", path.display());
428 }
429 return true;
430 }
431
432 false
433 }
434
435 fn add_new_file(&mut self, file: &Path) -> Result<(), RifError> {
437 self.meta.to_be_registerd.insert(file.to_owned());
438 Ok(())
439 }
440
441 fn remove_file(&mut self, file: &Path) -> Result<(), RifError> {
443 self.relation.remove_file(file)?;
444 self.history.remove_file(file)?;
445 Ok(())
446 }
447
448 fn register_new_file(&mut self, file: &Path, message: Option<&str>) -> Result<(), RifError> {
450 let mut closure = |entry_path : PathBuf| -> Result<LoopBranch, RifError> {
452 let striped_path = utils::relativize_path(&entry_path)?;
453
454 if let Some(_) = self.black_list.get(&striped_path) {
458 if striped_path.is_dir() {
459 return Ok(LoopBranch::Exit);
460 }
461 else {
462 return Ok(LoopBranch::Continue);
463 }
464 }
465
466 if !self.relation.add_file(&striped_path)? { return Ok(LoopBranch::Continue); }
467 Ok(LoopBranch::Continue)
468 }; if file.is_dir() {
473 utils::walk_directory_recursive(file, &mut closure)?;
474 } else {
475 let file = utils::relativize_path(file)?;
476
477 self.relation.add_file(&file)?;
481 self.history.add_history(&file, message.unwrap_or(""))?;
482 }
483 Ok(())
484 }
485
486 fn add_directory(&mut self, dir: &Path) -> Result<(), RifError> {
488 let tracked = self.relation.files.keys().cloned().collect::<Vec<PathBuf>>();
489 let modified = self.relation.get_modified_files()?.clone();
490 let mut deleted = self.relation.get_deleted_files().clone();
491 let mut to_be_deleted = HashSet::new();
492 let mut to_be_added = HashSet::new();
493 let mut to_be_registerd = HashSet::new();
494 let mut closure = |entry_path : PathBuf| -> Result<LoopBranch, RifError> {
496 let striped_path = utils::relativize_path(&entry_path)?;
497 if let Some(_) = self.black_list.get(&striped_path) {
501 if striped_path.is_dir() {
502 return Ok(LoopBranch::Exit);
503 }
504 else {
505 return Ok(LoopBranch::Continue);
506 }
507 }
508
509 if striped_path.is_dir() {
511 deleted.retain(|path| {
514 let is_inside = path.starts_with(&entry_path);
515 if is_inside {
517 to_be_deleted.insert(path.to_owned());
518 }
519 !is_inside
521 });
522 return Ok(LoopBranch::Continue);
523 }
524
525 if modified.contains(&striped_path) {
526 to_be_added.insert(striped_path);
528 } else if !tracked.contains(&striped_path) {
529 to_be_registerd.insert(striped_path);
531 }
532 Ok(LoopBranch::Continue)
533 }; utils::walk_directory_recursive(dir, &mut closure)?;
536
537 self.meta.to_be_registerd.extend(to_be_registerd);
538 self.meta.to_be_added.extend(to_be_added);
539 self.meta.to_be_deleted.extend(to_be_deleted);
540
541 Ok(())
542 }
543
544 fn add_old_file(&mut self, file: &Path, force: bool) -> Result<(), RifError> {
546 if file.exists() {
547 self.meta.queue_added(file, force);
548 } else {
549 self.meta.queue_deleted(file);
550 }
551 Ok(())
552 }
553
554 }