hotspots-discovery 0.0.6

A tool to find hotspots in your git repository.
Documentation
use log::debug;
use rayon::prelude::*;
use std::{ops::Not, path::Path, result, time::Instant};
use walkdir::{DirEntry, WalkDir};

#[derive(Debug, Eq, PartialEq)]
pub enum Lang {
    Rust,
    Go,
    Lua,
    Undefined,
}

impl From<&str> for Lang {
    fn from(value: &str) -> Self {
        match value {
            "go" => Lang::Go,
            "rust" => Lang::Rust,
            "lua" => Lang::Lua,
            _ => Lang::Undefined,
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
pub struct File {
    pub path: String,
    pub lang: Lang,
}

#[derive(Default)]
pub struct Discovery {
    prefixes: Vec<String>,
    not_contains: Vec<String>,
}

impl Discovery {
    pub fn with_prefix(&mut self, p: String) {
        self.prefixes.push(p);
    }
    pub fn not_contains(&mut self, p: String) {
        self.not_contains.push(p);
    }

    pub fn discover<P: AsRef<Path>>(&self, path: P) -> Option<Vec<File>> {
        let start = Instant::now();
        let res: Vec<File> = WalkDir::new(&path)
            .into_iter()
            .par_bridge()
            .filter_map(result::Result::ok)
            .filter(|p| {
                if self.prefixes.is_empty() {
                    true
                } else {
                    self.prefixes
                        .iter()
                        .any(|prefix| p.path().starts_with(prefix))
                }
            })
            .filter(|p| {
                if self.not_contains.is_empty() {
                    true
                } else {
                    self.not_contains
                        .iter()
                        .any(|prefix| p.path().to_str().unwrap_or("").contains(prefix))
                        .not()
                }
            })
            .filter(is_project_file)
            .filter_map(|p| {
                let lang = match detect_lang::from_path(p.path()) {
                    Some(lang) => lang.id().into(),
                    None => Lang::Undefined,
                };
                let p = p.path().to_str();

                p.map(|path| File {
                    path: path.to_owned(),
                    lang,
                })
            })
            .collect();

        debug!("Discovery took {:?}", start.elapsed());

        if res.is_empty() {
            None
        } else {
            Some(res)
        }
    }
}

fn is_project_file(entry: &DirEntry) -> bool {
    if entry.file_type().is_dir() {
        return false;
    }
    entry
        .file_name()
        .to_str()
        .map(|s| !s.starts_with('.'))
        .unwrap_or(false)
}

#[cfg(test)]
mod tests;