diffdir 0.4.4

deep compare two directories for differences
Documentation
use std::path::PathBuf;

use glob::Pattern;
use std::fs::File;
use std::io::{BufReader, Read};
use clap::*;

#[derive(Parser)]
#[clap(author, version, about = "A cli tool to compare two directories", long_about)]
pub struct Args {
    #[clap(value_parser, value_name = "dir a")]
    pub dir_a: PathBuf,

    #[clap(value_parser, value_name = "dir b")]
    pub dir_b: PathBuf,

    #[clap(long = "ignore", value_parser, use_value_delimiter(true), value_delimiter = ' ', num_args=1..)]
    pub ignore_patterns: Option<Vec<Pattern>>,

    #[clap(long = "ignore-file", value_parser)]
    pub ignore_file: Option<PathBuf>,

    /// Surpress output
    #[clap(long = "quiet", value_parser)]
    pub quiet: bool,

    /// will not format into ansi string and / or include colors
    #[clap(long = "no-colors", value_parser)]
    pub no_colors: bool,
}

impl Args {
    pub fn verify(&self) -> Result<(), String> {
        let mut errors : Vec<String> = Vec::new();
        if !self.dir_a.exists() {
            errors.push("argument error: Dir A doesn't exist".to_string());
        }
        if !self.dir_a.is_dir() {
            errors.push("argument error: A is not a directory".to_string());
        }
        if !self.dir_b.exists() {
            errors.push("argument error: Dir B doesn't exist".to_string());
        }
        if !self.dir_b.is_dir() {
            errors.push("argument error: B is not a directory".to_string());
        }
        if let Some(ignore_file) = &self.ignore_file {
            if !ignore_file.exists() {
                errors.push("argument error: Ignore file doesn't exist".to_string());
            }
        }
        if errors.is_empty() {
            return Ok(());
        }
        Err(errors.join("\n"))
    }
    pub fn parse_ignore_file(path: PathBuf) -> Vec<Pattern> {
        let file = File::open(path).expect("Error opening ignore file");
        let mut reader = BufReader::new(file);

        let mut patterns = Vec::new();
        let mut file_contents: String = String::new();
        _ = reader.read_to_string(&mut file_contents);
        for line in file_contents.lines() {
            if line.trim().is_empty() || line.starts_with('#') {
                continue;
            }
            match Pattern::new(&line) {
                Ok(pattern) => patterns.push(pattern),
                Err(e) => eprintln!("Invalid pattern in ignore file, line - '{}': {}", line, e),
            }
        }
        patterns
    }
}