lib_file/
lib.rs

1
2//! # Functions for use in Managing File IO
3//! The functions in the modules below were written to help
4//! manage file paths, file names, and other file-based
5//! operations.  I've used them in several different projects
6//! which is why I've kept them together in a separate crate.
7//! Their greatest weakness is poor error handling, so keep that
8//! in mind if you choose to use them.  By the way, I need help getting
9//! those weaknesses corrected, so if you feel like taking that on,
10//! please check
11//! out the issues tab in this crate's repository.
12//!
13//!
14//!    * VERSION = "0.0.5";
15//!    * AUTHOR = "John T. Reagan";
16//!    * LICENSE = "MIT";
17//!    * LICENSE_URL = "<https://opensource.org/licenses/MIT>";
18//!    * COPYRIGHT = "Copyright (c) 2025, John T. Reagan";
19//!    * REPOSITORY = "<https://github.com/jtreagan/lib_file>";
20
21/// # Functions based on the fltk::dialog module.
22pub mod file_fltk {
23
24    use fltk::dialog;
25    use std::path::Path;
26
27    /// Browse to a desired directory, return a string to use as a path for saving.
28    pub fn file_browse_save(usedir: &String) -> String {
29
30        // region Convert the text of the starting directory into a PATH & check exists.
31        let strtpath = Path::new(usedir.as_str());
32        if !strtpath.exists() {
33            eprintln!("The path {} does not exist!", strtpath.display());
34        }
35        // endregion
36
37        // Set the dialog browser to the default directory.
38        let mut dialog = dialog::NativeFileChooser
39                                ::new(dialog::NativeFileChooserType
40                                ::BrowseSaveFile);
41
42        dialog.set_preset_file(usedir); // Set the suggested file name
43
44        let setrslt = dialog.set_directory(&strtpath);
45        if let Err(e) = setrslt {
46            eprintln!("Failed to set starting directory to:  {}", e);
47        }
48
49        dialog.show();
50
51        let path = dialog.filename().to_str().unwrap().to_string();
52        path
53    }
54
55    /// Browse to a desired directory, filter the directory by the passed extension,
56    /// return a string to use as a path for saving.
57    ///
58    /// Examples:
59    ///
60    ///     let path = file_browse_save_fltr("Text Files   \t*.txt\nVariable Files   \t*.vrbl\nAll Files");
61    ///
62    /// or use something like this:
63    ///
64    ///     let path = file_browse_save_fltr("*.*");
65    ///
66    /// To show all files.
67    ///
68    pub fn file_browse_save_fltr(usedir: &String, extension: &str) -> String{
69        // Note that the `extension` value must have format  "*.xxxxx"
70
71        // region Convert the text of the starting directory into a PATH.
72        let strtpath = Path::new(usedir.as_str());
73        if !strtpath.exists() {
74            eprintln!("The path {} does not exist!", strtpath.display());
75        }
76        // endregion
77
78        // Set the dialog browser to the default directory.
79        let mut dialog = dialog::NativeFileChooser
80                                ::new(dialog::NativeFileChooserType
81                                ::BrowseSaveFile);
82
83        dialog.set_preset_file(usedir); // Set the suggested file name
84
85        dialog.set_filter(extension);
86        let setrslt = dialog.set_directory(&strtpath);
87        if let Err(e) = setrslt {
88            eprintln!("Failed to set starting directory to:  {}", e);
89        }
90
91        dialog.show();
92
93        let path = dialog.filename().to_str().unwrap().to_string();
94        path
95    }
96
97    /// Browse to a desired directory, return a string to use as a path.
98    /// Returned string includes both the path and the file name.
99    pub fn file_fullpath(usedir: &String) -> String {
100
101        // Convert the text of the starting directory into a PATH.
102        let strtpath = Path::new(usedir.as_str());
103        if !strtpath.exists() {
104            eprintln!("The path {} does not exist!", strtpath.display());
105        }
106
107        // Set the dialog browser to the default directory.
108        let mut dialog = dialog::NativeFileChooser::new(dialog
109                        ::NativeFileChooserType::BrowseFile);
110        let setrslt = dialog.set_directory(&strtpath);
111        if let Err(e) = setrslt {
112            eprintln!("Failed to set starting directory to:  {}", e);
113        }
114
115        dialog.show();
116
117        let path = dialog.filename().to_str().unwrap().to_string();
118
119        path
120    }
121
122    /// Browse to a desired directory, return a string to use as a path for saving.
123    /// Returned string includes both the path only, without the file name.
124    pub fn file_pathonly(usedir: &String) -> String {
125        // Convert the RefCell contents to a String.
126        //let rc_contents: String = usedir.borrow().clone();
127
128        // Convert the text of the starting directory into a PATH.
129        let strtpath = Path::new(usedir.as_str());
130        if !strtpath.exists() {
131            eprintln!("The path {} does not exist!", strtpath.display());
132        }
133
134        // Set the dialog browser to the default directory.
135        let mut dialog = dialog::NativeFileChooser::new(dialog
136                        ::NativeFileChooserType::BrowseFile);
137        let setrslt = dialog.set_directory(&strtpath);
138        if let Err(e) = setrslt {
139            eprintln!("Failed to set starting directory to:  {}", e);
140        }
141
142        dialog.show();
143
144        let path = dialog.filename();
145        let pathonly = path.parent().unwrap().to_str().unwrap().to_string();
146        pathonly
147    }
148
149    /// Browse to a desired directory, return the chosen file name only.
150    pub fn file_nameonly(usedir: &String) -> String {
151        // Convert the RefCell contents to a String.
152        //let rc_contents: String = usedir.borrow().clone();
153
154        // Convert the text of the starting directory into a PATH.
155        let strtpath = Path::new(usedir.as_str());
156        if !strtpath.exists() {
157            eprintln!("The path {} does not exist!", strtpath.display());
158        }
159
160        // Set the dialog browser to the default directory.
161        let mut dialog = dialog::NativeFileChooser::new(dialog
162                        ::NativeFileChooserType::BrowseFile);
163        let setrslt = dialog.set_directory(&strtpath);
164        if let Err(e) = setrslt {
165            eprintln!("Failed to set starting directory to:  {}", e);
166        }
167
168        dialog.show();
169
170        let path = dialog.filename();
171
172        let filename = path.file_name().expect("The path has no file available.");
173        let filename_str = filename.to_str().expect("The path is not valid UTF-8");
174        let filename_string: String = filename_str.to_string();
175
176        filename_string
177    }
178
179    /// Browse to a desired directory, filter the files by the passed extension.
180    /// The returned string includes both the path and the file name.
181    pub fn file_fullpath_fltr(usedir: &String, extension: &str) -> String {
182        // Note that the `extension` value must have format  `*.xxxxx`.
183
184        // Convert the RefCell contents to a String.
185        //let rc_contents: String = usedir.borrow().clone();
186
187        // Convert the text of the starting directory into a PATH.
188        let strtpath = Path::new(usedir.as_str());
189        if !strtpath.exists() {
190            eprintln!("The path {} does not exist!", strtpath.display());
191        }
192
193        // Set the dialog browser to the default directory.
194        let mut dialog = dialog::NativeFileChooser::new(dialog
195                        ::NativeFileChooserType::BrowseFile);
196        let setrslt = dialog.set_directory(&strtpath);
197        if let Err(e) = setrslt {
198            eprintln!("Failed to set starting directory to:  {}", e);
199        }
200        dialog.set_filter(extension);
201
202        dialog.show();
203
204        let path = dialog.filename().to_str().unwrap().to_string();
205        path
206    }
207
208    /// Browse to a desired directory, filter the files by the passed extension.
209    /// The returned string includes both the path only.
210    pub fn file_pathonly_fltr(usedir: &String, extension: &str) -> String {
211        // Note that the `extension` value must have format  `*.xxxxx`.
212
213        // Convert the RefCell contents to a String.
214        //let rc_contents: String = usedir.borrow().clone();
215
216        // Convert the text of the starting directory into a PATH.
217        let strtpath = Path::new(usedir.as_str());
218        if !strtpath.exists() {
219            eprintln!("The path {} does not exist!", strtpath.display());
220        }
221
222        // Set the dialog browser to the default directory.
223        let mut dialog = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile);
224        dialog.set_filter(extension);
225        let setrslt = dialog.set_directory(&strtpath);
226        if let Err(e) = setrslt {
227            eprintln!("Failed to set starting directory to:  {}", e);
228        }
229
230        dialog.show();
231
232        let path = dialog.filename();
233        let pathonly = path.parent().unwrap().to_str().unwrap().to_string();
234        pathonly
235    }
236
237    /// Browse to a desired directory, filter the files by the passed extension.
238    /// The returned string includes only the file name.
239    pub fn file_nameonly_fltr(usedir: &String, extension: &str) -> String {
240        // Note that the `extension` value must have format  `*.xxxxx`.
241
242        // Convert the RefCell contents to a String.
243        //let rc_contents: String = usedir.borrow().clone();
244
245        // Convert the text of the starting directory into a PATH.
246        let strtpath = Path::new(usedir.as_str());
247        if !strtpath.exists() {
248            eprintln!("The path {} does not exist!", strtpath.display());
249        }
250
251        // Set the dialog browser to the default directory.
252        let mut dialog = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile);
253        dialog.set_filter(extension);
254        let setrslt = dialog.set_directory(&strtpath);
255        if let Err(e) = setrslt {
256            eprintln!("Failed to set starting directory to:  {}", e);
257        }
258
259        dialog.show();
260
261        let path = dialog.filename();
262
263        let filename = path.file_name().expect("The path has no file available.");
264        let filename_str = filename.to_str().expect("The path is not valid UTF-8");
265        let filename_string: String = filename_str.to_string();
266
267        filename_string
268    }
269
270
271}
272
273/// # Terminal-based file i/o functions.
274pub mod file_mngmnt {
275
276//! ### Note the following:
277//! 1) I wrote these functions early-on while I was still
278//!     learning Rust and the code quality reflects that.
279//! 2) While the previous module -- file_fltk -- is
280//!     dependent on the FLTK-RS crate, the functions in
281//!     this module rely on the Rust standard crates along with
282//!     some functions from `lib_utils`, another of my personal
283//!     crates.  These functions are all terminal-based.
284
285    use lib_utils::{input_utilities::*, misc::*};
286    use std::io::{BufRead, BufReader, Read, Write};
287    use std::{fmt::Debug, fs, fs::File, io, path::Path, str::FromStr};
288    use std::cell::RefCell;
289    use std::rc::Rc;
290
291    /// Read a file to a String and print that String to the terminal.
292    ///
293    pub fn file_read_print_to_term(fname: String) {
294        let mut file = File::open(fname.as_str()).expect("Can't open file!");
295        let mut contents = String::new();
296        file.read_to_string(&mut contents).expect("Oops!  Cant read file...");
297
298        println!("{}", contents);
299    }
300
301    /// Read a file to a String with the file name passed
302    /// to the function as a RefCell.
303    pub fn file_read_file_to_string_refcell(fname: &Rc<RefCell<String>>) -> String {
304        let usefname = fname.borrow().clone();
305
306        let mut file = File::open(usefname.as_str()).expect("Can't open file!");
307        let mut contents = String::new();
308        file.read_to_string(&mut contents).expect("Oops!  Cant read file...");
309        contents
310    }
311
312    /// Read a comma delimited file and collect its contents into a vector.
313    ///
314    /// Example:
315    ///
316    ///     fn main() {
317    ///     // Replace the path below with your own file path to
318    ///     // a *.csv (comma separated values) file.
319    ///
320    ///         let file_path = "/home/somebody/somewhere/rusty/nails.csv";
321    ///         let vec = file_read_csv_to_vector(file_path);
322    ///
323    ///         println!("\n {:?} \n", vec); // Print the resulting vector
324    ///     }
325    pub fn file_read_csv_to_vector(file_path: &str) -> Vec<String> {  // Comma delimited
326        // Read the file into a string
327        let content = fs::read_to_string(file_path).expect("Failed to read the file");
328
329        // Split the content by commas and collect into a vector
330        content.split(',')
331            .map(|s| s.trim().to_string()) // Trim whitespace off each element
332            .collect()
333    }
334
335    /// Read a file's contents into a String and return
336    /// it as a String.
337    ///
338    /// Example:
339    ///
340    ///     fn main() {
341    ///     let filename = "/home/jtreagan/programming/rust/mine/tr_rbld1/David_config.yaml";
342    ///
343    ///     match file_read_to_string(filename) {
344    ///         Ok(contents) => {
345    ///            println!("\n The file contents is:  \n{} \n", contents);
346    ///        }
347    ///        Err(err) => {
348    ///            eprintln!("\n Error reading the file: {} \n", err);
349    ///        }
350    ///     }
351    ///     }
352    ///
353    pub fn file_read_to_string(fname: &str) -> io::Result<String> {
354        // TODO: This needs better error handling.
355
356        // Attempt to open the file
357        let mut file = File::open(fname)?;
358
359        // Prepare a String to store the file's contents
360        let mut contents = String::new();
361
362        // Read the file's contents into the string
363        file.read_to_string(&mut contents)?;
364
365        // Return the contents
366        Ok(contents)
367    }
368
369    /// Pull the file name off of the end of a path and return it.
370    ///
371    pub fn file_path_to_fname(pathstr: &String) -> String {
372        // todo: This doesn't look right.  Check it out.
373        let usepath = Path::new(pathstr);
374
375        match usepath.file_name() {
376            Some(filename_osstr) => {
377                match filename_osstr.to_str() {
378                    Some(filename_str) => filename_str.to_string(),
379                    None => "Error: Could not convert filename to UTF-8 string.".to_string(),
380                }
381            }
382            None => "Error: could not get filename".to_string()
383        }
384    }
385
386    /// Read a folder directory and collect the filenames into a vector.
387    /// Then return the vector.
388    ///
389    ///             ******* Example for file_get_dir_list() ******
390    ///
391    ///     fn main() {
392    ///         let dirpath = "../qbnk_list";
393    ///         let file_names = file_get_dir_list(dirpath);
394    ///
395    ///         println!("\n In main() the list of files is \n {:?}", file_names);
396    ///     }
397    pub fn file_get_dir_list(path: &str) -> Vec<String> {
398        let dir_entries = fs::read_dir(path).unwrap();
399
400        let file_names: Vec<String> = dir_entries
401            .filter_map(Result::ok)
402            .filter(|entry| entry.file_type().unwrap().is_file())
403            .map(|entry| entry.file_name().into_string().unwrap())
404            .collect();
405
406        file_names
407    }
408
409    /// Create a menu from a vector of file names.  Returns the item chosen
410    /// by the user as a String.
411    ///
412    /// Example:
413    ///
414    ///     fn main() {
415    ///         let dirpath = "../qbnk_list";
416    ///         let file_names = file_get_list(dirpath);
417    ///         let chosen: String;
418    ///
419    ///         chosen = file_namemenu(&file_names);
420    ///
421    ///         println!("\n The chosen menu item is:   {}", chosen);
422    ///     }
423    ///
424    pub fn file_namemenu(fnames: &Vec<String>) -> String {
425        let choice = activity_menu(&fnames, "\n Please choose which file you want to use \n");
426        let chosen = &fnames[choice - 1];
427        chosen.to_string()
428    }
429
430    /// Given a list of file names, this functioncollects all extensions
431    /// into a vector
432    /// and returns the vector.
433    ///
434    /// Example:
435    ///
436    ///          fn main() {
437    ///            let mountains = vec!["Soldier.mtn".to_string(),
438    ///                             "Deer.low".to_string(),
439    ///                             "Buttercup.mtn".to_string(),
440    ///                             "Borah.hgher".to_string(),
441    ///                             "Newman.low".to_string(),
442    ///                             "Dollarhide.mtn".to_string()];
443    ///
444    ///            let extns = file_extract_extensions(&mountains);
445    ///
446    ///            println!("\n In main() the list of extensions is \n {:?}", extns);
447    ///         }
448    ///
449    pub fn file_extract_extensions(filelist: &Vec<String>) -> Vec<String> {
450        let mut extensions: Vec<String> = Vec::new();
451
452        for item in filelist {
453            extensions.push(item.split('.').last().unwrap().to_string());
454        }
455        extensions
456    }
457
458    /// Removes the extension from a file name.  Returns the modified file name.
459    ///
460    pub fn file_remove_extension(filename: &str) -> String {
461        let path = Path::new(filename);
462        match path.file_stem() {
463            Some(stem) => stem.to_string_lossy().into_owned(),
464            None => String::from(filename),
465        }
466    }
467
468    /// Sorts a vector of file names by their extensions.
469    ///
470    ///     fn main() {
471    ///         let dirpath = "../qbnk_list";
472    ///         let mut file_names = file_get_list(dirpath);
473    ///
474    ///         println!("\n Before:   {:?}", file_names);
475    ///
476    ///         file_sort_by_ext(&mut file_names);
477    ///
478    ///         println!("\n After:   {:?}", file_names);
479    ///     }
480    ///
481    pub fn file_sort_by_ext(vctr: &mut Vec<String>) {
482        vctr.sort_by(|a, b| {
483            let ext_a = a.split('.').last().unwrap();
484            let ext_b = b.split('.').last().unwrap();
485            ext_a.cmp(ext_b)
486        });
487    }
488
489    /// Deletes all elements from the given vector that do not have an
490    /// extension that matches the passed extension.
491    ///
492    ///
493    ///     fn main() {
494    ///        let dirpath = "../qbnk_list";
495    ///        let mut file_names = file_get_list(dirpath);
496    ///
497    ///        println!("\n Before:   {:?}", file_names);
498    ///
499    ///       file_sort_by_ext(&mut file_names);
500    ///       file_del_unwanted_names(&mut file_names, "lst");
501    ///
502    ///       println!("\n After:   {:?}", file_names);
503    ///     }
504    ///
505    pub fn file_del_unwanted_names(vctr: &mut Vec<String>, keeper_ext: &str) {
506        vctr.retain(|item| (item.split('.').last().unwrap()) == keeper_ext);
507    }
508
509    /// Read the elements from a given file, storing them in a passed vector.
510    ///
511    pub fn file_read_to_vec<T: FromStr>(fname: &str, vctr: &mut Vec<T>)
512        where <T as FromStr>::Err: Debug {
513        let file = File::open(fname).unwrap();
514        let reader = BufReader::new(file);
515
516        for line in reader.lines() {
517            let line = line.unwrap();
518            let num = line.parse().unwrap();
519            vctr.push(num);
520        }
521    }
522
523    /// Saves a vector to a file.
524    ///
525    pub fn file_save_vec<T: std::fmt::Display>(fname: &str, vector: &[T]) ->
526                                                         std::io::Result<()> {
527        let mut file = File::create(fname)?;
528        for num in vector {
529            let num_str = num.to_string();
530            file.write_all(num_str.as_bytes())?;
531            file.write_all(b"\n")?;
532        }
533        Ok(())
534    }
535
536    /// Lets the user input a path string, checks that path for validity, then
537    /// lets the user choose a file to work with.  Returns both
538    /// the path and the chosen file name.
539    /// Example:
540    ///
541    ///     fn main() {
542    ///         let extension = "lst";  // Be sure to check using a non-existing extension.
543    ///         let existing_fname = file_choose_from_existing(&extension);
544    ///         if existing_fname == "".to_string() {
545    ///             return
546    ///         } else {
547    ///             println!("\n You chose the name   {}   \n", existing_fname);
548    ///         }
549    ///     }
550    pub fn file_choose_from_existing(extsn: &str) -> (String, String) {
551        let dirpath = input_string_prompt("Please enter the path for the directory where this file has been saved:  ");
552        let dirok = dir_checkexist_fix(&dirpath);
553        if dirok.0 == false {
554            println!("\n The path \n   {} \n was not usable and was not corrected. \n", dirpath);
555            panic!("Invalid and uncorrected path entered.");
556            // Maybe eventually return a result that the main program can use to
557            // redirect user's activity.
558        }
559
560        let is_empty = dir_check_empty(&dirpath).unwrap();
561        if is_empty {
562            println!("\n That directory is empty.");
563            return (dirpath, "".to_string());
564        }
565        let mut file_names = file_get_dir_list(dirpath.as_str());
566
567        file_del_unwanted_names(&mut file_names, extsn);
568        if file_names.len() == 0 {
569            println!("\n There are no *.{} files in this directory.", extsn);
570            return (dirpath, "".to_string());
571        }
572
573        let chosen = file_namemenu(&file_names);
574        (dirpath, chosen)
575    }
576
577    /// Choose a name for your file from existing files in a given directory.
578    ///
579    /// Choose a file name to use for saving.
580    /// The function adds an extension to the file name and then
581    /// appends it to the path.
582    ///
583    /// Example:
584    ///
585    ///         fn main() {
586    ///             let dirpath = file_choose_new_fname("lst");
587    ///             println!("\n In main() the new path & fname is:  {}", dirpath);
588    ///
589    ///             println!("\n All is okay!!  :>) \n");
590    ///         }
591    pub fn file_choose_new_fname(extnsn: &str, dirpath: &String) -> String {
592        let mut fname: String;
593        let mut usepath: String;
594        loop {
595            usepath = dirpath.clone();
596            fname = input_string_prompt("\n Please enter a name for your new file:  ");
597            if fname.split('.').last().unwrap() != extnsn {
598                fname = fname + "." + extnsn;
599            }
600            usepath = usepath + "/" + fname.as_str();  // Try using format!() here.
601            let fullpath = Path::new(&usepath);
602            let exists = fullpath.try_exists()
603                .expect("Error when checking the existence of the file");
604            if exists {
605                println!("\n That file   {}   already exists.", &fname);
606                let choice = input_bool_prompt("\n Do you want to overwrite the file? ");
607                if choice { break; }
608                else { continue; }
609            }
610            break;
611        }
612        usepath
613    }
614
615    /// Input a file name and append an extension to it.
616    ///
617    pub fn file_getfname_addextsn(extnsn: &str) -> String {
618        let mut fname = input_string_prompt("\n Please enter a name for your new file:  ");
619        if fname.split('.').last().unwrap() != extnsn {
620            fname = fname + "." + extnsn;
621        }
622        fname
623
624    }
625
626    /// Add an extension to a file name.
627    ///
628    pub fn file_addextsn(extnsn: &str, fname: &String) -> String {
629        let mut usename = fname.clone();
630        if usename.split('.').last().unwrap() != extnsn {
631            usename = usename + "." + extnsn;
632        }
633        usename
634    }
635
636    /// This function is not yet finished.  Don't use it.
637    ///
638    pub fn file_chkfname( fname: &String, dirpath: &String) -> String {
639// This is not yet ready.  What are you returning?
640
641        let mut usepath: String;
642        loop {
643            usepath = dirpath.clone();
644            usepath = usepath + "/" + fname.as_str();
645            let fullpath = Path::new(&usepath);
646            let exists = fullpath.try_exists()
647                .expect("Error when checking the existence of the file");
648            if exists {
649                println!("\n That file   {}   already exists.", &fname);
650                let choice = input_bool_prompt("\n Do you want to overwrite the file? ");
651                if choice {
652                    break;
653                }
654                else { continue; }
655            }
656            break;
657        }
658        usepath
659    }
660
661    /// Check the validity of a directory path and correct it if necessary.
662    ///
663    /// Example:
664    ///
665    ///     fn main() {
666    ///         let mut dirpath: String = "kjhkjhjkh/home/camascounty/programming/rust/mine/file_lib".to_string();
667    ///
668    ///         let dirchecked = dir_checkexist_fix(&dirpath);
669    ///         if dirchecked.0 == false {
670    ///             println!("\n The path \n      {} \n was not usable and was not corrected. \n", dirpath);
671    ///         } else {
672    ///            println!("\n The correct path is:  {}", dirchecked.1);
673    ///            println!("\n All is okay!!  :>) \n");
674    ///         }
675    ///     }
676    pub fn dir_checkexist_fix(dirpath: &String) -> (bool, String) {
677        let mut fullpath = Path::new(dirpath.as_str());
678        let mut newpath: String = dirpath.to_string().clone();
679
680        loop {
681            let exists = fullpath.try_exists()
682                .expect("Error when checking the existence of the directory");
683
684            if exists {
685                return (true, newpath);
686            } else {
687                println!("\n The directory \n      {} \n does not exist and may not be used.", newpath);
688                newpath = input_string_prompt(
689                    "\n Please enter a corrected path for the directory in which you wish to save this file.  \n\
690                         (Do not include the file name):   ");  // Eventually add ability to edit the existing string.
691                if newpath == "" {
692                    return (false, "".to_string());
693                } else {
694                    fullpath = Path::new(newpath.as_str());
695                }
696            }
697        }
698    }
699
700    /// Check to see if a directory is empty.
701    ///
702    /// Example:
703    ///
704    ///      fn main() {
705    ///         let directory = "/home/camascounty/programming/rust/mine/empty";
706    ///         match dir_check_empty(directory) {
707    ///         Ok(is_empty) => {
708    ///             if is_empty {
709    ///                 println!("\n The path  {}  is empty.", directory);
710    ///             } else {
711    ///                 println!("\n The path  {}  is not empty.", directory);
712    ///             }
713    ///         }
714    ///         Err(err) => {
715    ///             println!("Error when checking if directory is empty: {}", err);
716    ///         }
717    ///     }
718    ///     }
719    ///
720    pub fn dir_check_empty(dirpath: &str) -> io::Result<bool> {
721        let mut entries = fs::read_dir(dirpath)?;
722        let first_entry = entries.next();
723        Ok(first_entry.is_none())
724    }
725
726    /// Check a user-entered path for validity.
727    ///
728    pub fn dir_get_path() -> (bool, String) {
729        let dirpath = input_string_prompt(
730            "\n Please enter a path for the directory in which you wish to save this file.  \n\
731              (Do not include the file name):   ");
732
733        let dirok = dir_checkexist_fix(&dirpath);
734        if dirok.0 == false {
735            println!("\n The path \n   {} \n was not usable and was not corrected. \n", dirpath);
736            panic!("Invalid and uncorrected path entered.");
737            // todo: Maybe eventually return an error that the main program can use to
738            // redirect the user's activity.
739        }
740        (true, dirpath)
741    }
742
743    /// Same as `dir_get_path` except that one can pass whatever prompt
744    /// you like to the function.
745    pub fn dir_get_path_prompt(prompt: &str) -> (bool, String) {
746        let dirpath = input_string_prompt( prompt);
747        let dirok = dir_checkexist_fix(&dirpath);
748        if dirok.0 == false {
749            println!("\n The path \n   {} \n was not usable and was not corrected. \n", dirpath);
750            panic!("Invalid and uncorrected path entered.");
751            // todo: Maybe eventually return an error that the main program can use to
752            // redirect user's activity.
753        }
754        (true, dirpath)
755    }
756
757
758
759}
760