ragit/index/commands/
remove.rs1use super::Index;
2use crate::error::Error;
3use crate::index::{CHUNK_DIR_NAME, IIStatus};
4use ragit_fs::{exists, get_relative_path, remove_file, set_extension};
5use std::collections::HashSet;
6
7pub type Path = String;
8
9#[derive(Clone, Copy, Default)]
10pub struct RemoveResult {
11 pub staged: usize,
12 pub processed: usize,
13}
14
15impl Index {
16 pub fn remove_file(
17 &mut self,
18 path: Path,
19 dry_run: bool,
20 recursive: bool,
21 auto: bool,
22 staged: bool,
23 processed: bool,
24 ) -> Result<RemoveResult, Error> {
25 let mut rel_path = get_relative_path(&self.root_dir, &path)?;
26 let (mut staged_candidates, mut processed_candidates) = if recursive {
27 if !rel_path.ends_with("/") {
28 rel_path = format!("{rel_path}/");
29 }
30
31 let mut staged_candidates = vec![];
32 let mut processed_candidates = vec![];
33
34 if rel_path == "/" || rel_path == "" {
37 staged_candidates = self.staged_files.iter().map(|f| f.to_string()).collect();
38 processed_candidates = self.processed_files.keys().map(|f| f.to_string()).collect();
39 }
40
41 else {
42 for file in self.staged_files.iter() {
43 if file.starts_with(&rel_path) {
44 staged_candidates.push(file.to_string());
45 }
46 }
47
48 for file in self.processed_files.keys() {
49 if file.starts_with(&rel_path) {
50 processed_candidates.push(file.to_string());
51 }
52 }
53 }
54
55 (staged_candidates, processed_candidates)
56 } else {
57 let staged_candidates = if self.staged_files.contains(&rel_path) {
58 vec![rel_path.clone()]
59 } else {
60 vec![]
61 };
62 let processed_candidates = if self.processed_files.contains_key(&rel_path) {
63 vec![rel_path.clone()]
64 } else {
65 vec![]
66 };
67
68 (staged_candidates, processed_candidates)
69 };
70
71 if staged_candidates.is_empty() && processed_candidates.is_empty() {
72 return Err(Error::NoSuchFile { path: Some(path), uid: None });
73 }
74
75 if !staged {
76 staged_candidates = vec![];
77 }
78
79 if !processed {
80 processed_candidates = vec![];
81 }
82
83 if auto {
84 let mut staged_candidates_new = vec![];
85 let mut processed_candidates_new = vec![];
86
87 for file in staged_candidates.into_iter() {
88 if !exists(&Index::get_data_path(&self.root_dir, &file)?) {
89 staged_candidates_new.push(file)
90 }
91 }
92
93 for file in processed_candidates.into_iter() {
94 if !exists(&Index::get_data_path(&self.root_dir, &file)?) {
95 processed_candidates_new.push(file)
96 }
97 }
98
99 staged_candidates = staged_candidates_new;
100 processed_candidates = processed_candidates_new;
101 }
102
103 if !dry_run {
104 let staged_candidates: HashSet<_> = staged_candidates.iter().collect();
105 self.staged_files = self.staged_files.iter().filter(
106 |file| !staged_candidates.contains(file)
107 ).map(
108 |file| file.to_string()
109 ).collect();
110
111 self.ii_status = IIStatus::Outdated;
112
113 for file in processed_candidates.iter() {
114 match self.processed_files.get(file).map(|uid| *uid) {
115 Some(file_uid) => {
116 for uid in self.get_chunks_of_file(file_uid)? {
117 self.chunk_count -= 1;
118 let chunk_path = Index::get_uid_path(
119 &self.root_dir,
120 CHUNK_DIR_NAME,
121 uid,
122 Some("chunk"),
123 )?;
124 remove_file(&chunk_path)?;
125 let tfidf_path = set_extension(&chunk_path, "tfidf")?;
126
127 if exists(&tfidf_path) {
128 remove_file(&tfidf_path)?;
129 }
130 }
131
132 self.processed_files.remove(file).unwrap();
133 self.remove_file_index(file_uid)?;
134 },
135 _ => {},
136 }
137 }
138
139 self.reset_uid(false )?;
140 self.save_to_file()?;
141
142 if self.chunk_count == 0 {
144 self.ii_status = IIStatus::Complete;
145 }
146 }
147
148 Ok(RemoveResult {
149 staged: staged_candidates.len(),
150 processed: processed_candidates.len(),
151 })
152 }
153}
154
155impl RemoveResult {
156 pub fn is_empty(&self) -> bool {
157 self.staged == 0 && self.processed == 0
158 }
159}
160
161impl std::ops::AddAssign<Self> for RemoveResult {
162 fn add_assign(&mut self, rhs: Self) {
163 self.staged += rhs.staged;
164 self.processed += rhs.processed;
165 }
166}