11use std:: {
2- fs:: { self , File } , io:: { self , BufReader , BufWriter , Read } , path:: Path , process:: { exit, ExitCode } , str:: { self } , sync:: { Arc , Mutex } , thread
2+ collections :: HashSet , fs:: { self , File } , io:: { self , BufReader , BufWriter , Read } , path:: { Path , PathBuf } , process:: { exit, ExitCode } , str:: { self } , sync:: { Arc , Mutex } , thread
33} ;
44
55use parser:: { Cli , Commands } ;
@@ -123,7 +123,7 @@ fn save_model_as_json(model: &InMemoryModel, index_path: &str) -> Result<(), ()>
123123const ALLOWED_FILE_TYPE_EXTENSIONS : [ & str ; 5 ] = [ "xml" , "xhtml" , "txt" , "md" , "pdf" ] ;
124124
125125/* Indexes a folder as a json file and adds to model, Processed is the number of file indexed */
126- fn append_folder_to_model ( dir_path : & Path , model : Arc < Mutex < InMemoryModel > > , processed : & mut usize ) -> Result < ( ) , ( ) > {
126+ fn append_folder_to_model ( dir_path : & Path , model : Arc < Mutex < InMemoryModel > > , processed : & mut usize , visited : & mut HashSet < PathBuf > ) -> Result < ( ) , ( ) > {
127127 let dir = fs:: read_dir ( dir_path) . map_err ( |err| {
128128 eprintln ! ( "{}: Failed to read directory {dir_path} as \" {err}\" " , "ERROR" . bold( ) . red( ) ,
129129 dir_path = dir_path. to_str( ) . unwrap( ) . bold( ) . bright_blue( ) ,
@@ -174,7 +174,7 @@ fn append_folder_to_model(dir_path: &Path, model: Arc<Mutex<InMemoryModel>>, pro
174174
175175 // Recursively index all the folders
176176 if file_type. is_dir ( ) {
177- append_folder_to_model ( & file_path, Arc :: clone ( & model) , processed) ?;
177+ append_folder_to_model ( & file_path, Arc :: clone ( & model) , processed, visited ) ?;
178178 continue ' step;
179179 }
180180
@@ -184,6 +184,8 @@ fn append_folder_to_model(dir_path: &Path, model: Arc<Mutex<InMemoryModel>>, pro
184184 file_path. display( ) . to_string( ) . bright_blue( ) ,
185185 err. to_string( ) . red( ) ) ;
186186 } ) ?;
187+
188+ visited. insert ( file_path. clone ( ) ) ;
187189
188190 // Main
189191 let mut model = model. lock ( ) . unwrap ( ) ;
@@ -234,6 +236,42 @@ fn fetch_model(index_path: &str) -> Result<InMemoryModel, ()> {
234236 return Ok ( model) ;
235237}
236238
239+ fn index_directory ( dir_path : & Path , model : Arc < Mutex < InMemoryModel > > , index_path : Option < & str > ) -> Result < ( ) , ( ) > {
240+ let root_dir = fs:: canonicalize ( dir_path) . unwrap_or_else ( |err| {
241+ eprintln ! ( "{}: Could not canonicalize root dir {} as {}" , "ERROR" . bold( ) . red( ) , dir_path. display( ) . to_string( ) . bright_blue( ) , err. to_string( ) . red( ) ) ;
242+ exit ( 1 ) ;
243+ } ) ;
244+
245+ let mut processed: usize = 0 ;
246+ let mut visited: HashSet < PathBuf > = HashSet :: new ( ) ;
247+
248+ append_folder_to_model ( & root_dir, Arc :: clone ( & model) , & mut processed, & mut visited) ?;
249+
250+ // Purge deleted files
251+ let mut model_lock = model. lock ( ) . unwrap ( ) ;
252+ let mut to_remove = Vec :: new ( ) ;
253+
254+ for path in model_lock. docs . keys ( ) {
255+ if path. starts_with ( & root_dir) && !visited. contains ( path) {
256+ to_remove. push ( path. clone ( ) ) ;
257+ }
258+ }
259+
260+ for path in to_remove {
261+ model_lock. remove_document ( & path) ;
262+ println ! ( "{}: Removed deleted file {}" , "INFO" . cyan( ) , path. display( ) . to_string( ) . bright_yellow( ) ) ;
263+ processed += 1 ;
264+ }
265+
266+ // Save the model only when some files were added/modified/deleted
267+ if processed > 0 {
268+ if let Some ( p) = index_path {
269+ save_model_as_json ( & model_lock, p) ?;
270+ }
271+ }
272+ println ! ( "{}: Finished indexing ..." , "INFO" . cyan( ) ) ;
273+ Ok ( ( ) )
274+ }
237275
238276#[ derive( ValueEnum , Clone , Debug , PartialEq ) ]
239277enum RankMethod {
@@ -259,6 +297,16 @@ fn entry() -> Result<(), ()> {
259297 check_index ( & index_file_path) . unwrap ( ) ;
260298 }
261299
300+ Commands :: Index { dir_path, output_file } => {
301+ let model = Arc :: new ( Mutex :: new ( Default :: default ( ) ) ) ;
302+ let output_path = output_file. unwrap_or_else ( || {
303+ let mut p = Path :: new ( & dir_path) . to_path_buf ( ) ;
304+ p. push ( ".docsense.json" ) ;
305+ p. to_str ( ) . unwrap ( ) . to_string ( )
306+ } ) ;
307+ index_directory ( Path :: new ( & dir_path) , model, Some ( & output_path) ) ?;
308+ }
309+
262310 Commands :: Serve { dir_path, address , rank_method} => {
263311 // IDEATE: Is it fine to place the index file in the folder itself or place in a root dir?
264312 let mut index_path = Path :: new ( & dir_path) . to_path_buf ( ) ;
@@ -285,15 +333,8 @@ fn entry() -> Result<(), ()> {
285333 let model = Arc :: clone ( & model) ;
286334 let dir_path = dir_path. clone ( ) ;
287335 thread:: spawn ( move || {
288- let mut processed: usize = 0 as usize ;
289- append_folder_to_model ( Path :: new ( & dir_path) , Arc :: clone ( & model) , & mut processed) . unwrap ( ) ;
290-
291- // Save the model only when some files are processed
292- if processed > 0 {
293- let model= model. lock ( ) . unwrap ( ) ;
294- save_model_as_json ( & model, index_path. to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
295- }
296- println ! ( "{}: Finished indexing ..." , "INFO" . cyan( ) ) ;
336+ let index_str = index_path. to_str ( ) . unwrap ( ) . to_string ( ) ;
337+ index_directory ( Path :: new ( & dir_path) , model, Some ( & index_str) ) . unwrap ( ) ;
297338 } ) ;
298339 }
299340 // TODO: Print the information of server start at the end of logging
0 commit comments