1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::common::*;

use std::error::Error;
use std::path::PathBuf;

mod checks;
mod common;
mod fixes;
mod fs_utils;

pub use checks::available_check_names;

#[allow(clippy::redundant_closure)]
pub fn run(args: &clap::ArgMatches, current_dir: &PathBuf) -> Result<Vec<Warning>, Box<dyn Error>> {
    let mut file_paths: Vec<PathBuf> = Vec::new();
    let mut skip_checks: Vec<&str> = Vec::new();
    let mut excluded_paths: Vec<PathBuf> = Vec::new();

    let is_recursive = args.is_present("recursive");

    if let Some(skip) = args.values_of("skip") {
        skip_checks = skip.collect();
    }

    if let Some(excluded) = args.values_of("exclude") {
        excluded_paths = excluded
            .filter_map(|f| fs_utils::canonicalize(f).ok())
            .collect();
    }

    if let Some(inputs) = args.values_of("input") {
        let input_paths = inputs
            .filter_map(|s| fs_utils::canonicalize(s).ok())
            .collect();

        file_paths.extend(get_file_paths(input_paths, &excluded_paths, is_recursive));
    }

    let is_fix = args.is_present("fix");
    let mut warnings: Vec<Warning> = Vec::new();

    for path in file_paths {
        let relative_path = match fs_utils::get_relative_path(&path, &current_dir) {
            Some(p) => p,
            None => continue,
        };

        let (fe, strs) = match FileEntry::from(relative_path) {
            Some(f) => f,
            None => continue,
        };

        let mut lines = get_line_entries(&fe, strs);

        // run fixers & write results to file
        let mut result = checks::run(&lines, &skip_checks);
        if is_fix && !result.is_empty() && fixes::run(&mut result, &mut lines, &skip_checks) > 0 {
            // create backup copy unless user specifies not to
            let should_backup = !args.is_present("no-backup");
            if should_backup {
                let backup_file = fs_utils::backup_file(&fe)?;
                println!("Original file was backed up to: {:?}\n", backup_file);
            }

            // write corrected file
            fs_utils::write_file(&fe.path, lines)?;
        }

        warnings.extend(result);
    }

    Ok(warnings)
}

fn get_file_paths(
    dir_entries: Vec<PathBuf>,
    excludes: &[PathBuf],
    is_recursive: bool,
) -> Vec<PathBuf> {
    let nested_paths: Vec<PathBuf> = dir_entries
        .iter()
        .filter(|entry| entry.is_dir())
        .filter(|entry| !excludes.contains(entry))
        .filter_map(|dir| dir.read_dir().ok())
        .map(|read_dir| {
            read_dir
                .filter_map(|e| e.ok())
                .map(|e| e.path())
                .filter(|path| {
                    FileEntry::is_env_file(path)
                        || (is_recursive && path.is_dir() && path.read_link().is_err())
                })
                .collect()
        })
        .flat_map(|dir_entries| get_file_paths(dir_entries, excludes, is_recursive))
        .collect();

    let mut file_paths: Vec<PathBuf> = dir_entries
        .into_iter()
        .filter(|entry| entry.is_file())
        .filter(|entry| !excludes.contains(entry))
        .collect();

    file_paths.extend(nested_paths);
    file_paths.sort();
    file_paths.dedup();
    file_paths
}

fn get_line_entries(fe: &FileEntry, lines: Vec<String>) -> Vec<LineEntry> {
    let mut entries: Vec<LineEntry> = Vec::with_capacity(fe.total_lines);

    for (index, line) in lines.into_iter().enumerate() {
        entries.push(LineEntry {
            number: index + 1,
            file: fe.clone(),
            raw_string: line,
            is_deleted: false,
        })
    }

    entries
}