nolo 0.1.0

A CLI tool for discovering and analyzing `TODO` comments across codebases.
Documentation
use crate::error::NoloError;
use ignore::Walk;
use std::{
    io,
    path::{Path, PathBuf},
    process::Command,
};
use tracing::debug;

pub fn discover(paths: &Path) -> Result<Vec<PathBuf>, NoloError> {
    debug!("Discovering files in: {:?}", paths);

    if paths.is_file() {
        debug!("Input is a file, returning single file: {:?}", paths);
        Ok(vec![paths.to_path_buf()])
    } else if paths.is_dir() {
        debug!("Input is a directory, scanning recursively: {:?}", paths);
        collect_files(paths).map_err(|e| NoloError::FileDiscovery {
            path: paths.display().to_string(),
            source: e,
        })
    } else {
        Err(NoloError::FileDiscovery {
            path: paths.display().to_string(),
            source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"),
        })
    }
}

pub fn git_root() -> Option<String> {
    let output = Command::new("git")
        .arg("rev-parse")
        .arg("--show-toplevel")
        .output();
    match output {
        Ok(root) => Some(
            String::from_utf8(root.stdout)
                .unwrap()
                .trim_end()
                .to_owned(),
        ),
        Err(_) => None,
    }
}

fn collect_files<P: AsRef<Path>>(dir: P) -> io::Result<Vec<PathBuf>> {
    let mut files = Vec::new();

    for result in Walk::new(dir) {
        match result {
            Ok(entry) => {
                let path = entry.path();
                if path.is_file() {
                    files.push(path.to_path_buf());
                }
            }
            Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)),
        }
    }

    Ok(files)
}