Skip to main content

ferris_files/
config.rs

1use crate::args::Args;
2use crate::get_fd_limit;
3use std::collections::HashSet;
4use std::env;
5use std::error::Error;
6use std::fs::File;
7use std::io::{BufRead, BufReader};
8use std::path::PathBuf;
9
10/// Configuration structure containing runtime settings.
11///
12/// # Fields
13///
14/// * `num_threads` - Number of threads to use in parallel processing
15/// * `num_entries` - Number of entries to output at program completion
16/// * `batch_size` - Size of batches for processing file metadata
17/// * `root_path` - Base directory path to recursively find and size files
18/// * `skip_dirs` - Set of directory names to exclude from the search
19/// * `max_open_files` - Maximum number of open file handles used by this program
20/// * `verbose` - Bool to determine if errors collected during runtime will be printed
21///
22#[derive(Clone)]
23pub struct Config {
24    pub num_threads: usize,
25    pub num_entries: usize,
26    pub batch_size: usize,
27    pub root_path: PathBuf,
28    pub skip_dirs: HashSet<String>,
29    pub max_open_files: usize,
30    pub verbose: bool,
31}
32
33impl Config {
34    /// Builds a new Config instance from provided command line arguments.
35    ///
36    /// # Parameters
37    ///
38    /// * `args` - Reference to Args structure containing command line arguments
39    ///
40    /// # Returns
41    ///
42    /// * `Result<Config, Box<dyn Error>>` - New Config instance or error if construction fails
43    ///
44    /// # Details
45    ///
46    /// This function performs the following setup:
47    /// 1. Configures parallel processing based on available CPU cores
48    /// 2. Calls a library function to determine platform specific cap on open file descriptors
49    /// 3. Sets number of entries to output equal to provided command line arg or default of 10
50    /// 4. Sets batch size to match command line arg if specified or else default to 1000
51    /// 5. Sets verbose bool to match command line arg
52    /// 6. Sets up the root directory path for operations
53    /// 7. Loads directory exclusion rules if file containing dirs was supplied
54    ///
55    /// # Errors
56    ///
57    /// Returns an error if:
58    /// * Current directory cannot be determined when no target directory is specified
59    /// * Exclusion file cannot be opened or read
60    /// * Thread pool configuration fails (logged as error but doesn't halt execution)
61    /// 
62    pub fn build(args: &Args) -> Result<Config, Box<dyn Error>> {
63        let num_threads = std::thread::available_parallelism()
64            .map(|n| n.get())
65            .unwrap_or(1);
66
67        println!("Preparing to scan using {} threads", num_threads);
68
69        let max_open_files = get_fd_limit();
70        println!("Limiting open file handles to {}", max_open_files);
71
72        let num_entries = args.num_entries;
73        let batch_size = args.batch_size;
74        let verbose = args.verbose;
75
76        let root_path = if let Some(target_dir) = &args.target_dir {
77            PathBuf::from(target_dir)
78        } else {
79            env::current_dir()?
80        };
81
82        let mut skip_dirs: HashSet<String> = HashSet::new();
83        if let Some(exclusion_file) = &args.exclusion_file {
84            let file = File::open(exclusion_file)
85                .expect("A path to an excluded directories file was provided but the file could not be read");
86
87            let reader = BufReader::new(file);
88            reader.lines().for_each(|line| match line {
89                Ok(dir) => {
90                    skip_dirs.insert(dir);
91                }
92                Err(e) => log::error!("Error reading line: {}", e),
93            });
94        }
95
96        Ok(Config {
97            num_threads,
98            num_entries,
99            batch_size,
100            root_path,
101            skip_dirs,
102            max_open_files,
103            verbose
104        })
105    }
106}