xloc 0.2.0

A fast, multi-threaded line counting utility written in Rust.
Documentation
use std::fs;
use std::io::Result;
use std::path::{Path, PathBuf};

pub struct Counter {
    pub path: PathBuf,
    pub files: Vec<PathBuf>,
}

impl Counter {
    pub fn new(path: PathBuf) -> Self {
        Self {
            path,
            files: vec![],
        }
    }

    fn scan(&mut self, path: &Path) -> Result<()> {
        if path.is_file() {
            self.files.push(path.to_owned());
            return Ok(());
        }

        for entry in fs::read_dir(path)?.filter(|d| {
            let mut excluded = false;

            for exclude in ["target", ".git"] {
                excluded = !d.as_ref().unwrap().path().ends_with(exclude);

                if excluded {
                    break;
                }
            }

            excluded
        }) {
            let path = entry?.path();

            if path.is_dir() {
                self.scan(&path)?;
            } else {
                self.files.push(path);
            }
        }

        Ok(())
    }

    pub fn generate_workloads(&self, njobs: usize, nfiles: usize) -> Result<Vec<usize>> {
        let chunk_size = nfiles / njobs;
        let remainder = nfiles % njobs;
        let mut workloads = vec![chunk_size; njobs];

        for i in workloads.iter_mut().take(remainder) {
            *i += 1;
        }

        Ok(workloads)
    }

    pub fn count_files(&mut self) -> Result<usize> {
        self.scan(&self.path.clone())?;
        Ok(self.files.len())
    }
}

#[cfg(test)]
mod counter_tests {
    use std::{path::PathBuf, str::FromStr};

    use super::Counter;

    #[test]
    fn counter_new() {
        let path = PathBuf::from_str("tests/data").unwrap();
        let counter = Counter::new(path.clone());

        assert_eq!(counter.path, path);
        assert_eq!(counter.files.len(), 0);
    }

    #[test]
    fn counter_scan_dir() {
        let path = PathBuf::from_str("tests/data").unwrap();
        let mut counter = Counter::new(path.clone());

        let result = counter.scan(&path);
        assert!(result.is_ok());
    }

    #[test]
    fn counter_scan_file() {
        let path = PathBuf::from_str("tests/data/data.rs").unwrap();
        let mut counter = Counter::new(path.clone());

        let result = counter.scan(&path);
        assert!(result.is_ok());
    }

    #[test]
    fn counter_count_files() {
        let path = PathBuf::from_str("tests/data").unwrap();
        let mut counter = Counter::new(path.clone());

        let result = counter.count_files();
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 3);
    }

    #[test]
    fn counter_count_file() {
        let path = PathBuf::from_str("tests/data/data.rs").unwrap();
        let mut counter = Counter::new(path.clone());

        let result = counter.count_files();
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 1);
    }

    #[test]
    fn counter_generate_workloads() {
        let path = PathBuf::from_str("tests/data").unwrap();
        let counter = Counter::new(path.clone());

        let result = counter.generate_workloads(1, 3);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), vec![3]);

        let result = counter.generate_workloads(3, 3);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), vec![1, 1, 1]);

        let result = counter.generate_workloads(2, 3);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), vec![2, 1]);
    }
}