use {
    problemo::*,
    std::{error::Error, path::*},
};

// First see: examples/dedup.rs

// Here we're going to implement our own equality test using a PathBuf attachment

gloss_error!(FileError);

pub struct PathCauseEquality;

impl PathCauseEquality {
    /// Constructor.
    pub fn new<ErrorT>() -> CauseEquality
    where
        ErrorT: 'static + Error + PartialEq,
    {
        CauseEquality(Box::new(Self::eq_::<ErrorT>))
    }

    fn eq_<ErrorT>(left: &Cause, right: &Cause) -> bool
    where
        ErrorT: 'static + Error + PartialEq,
    {
        if let Some(left_error) = left.error.downcast_ref::<ErrorT>()
            && let Some(right_error) = right.error.downcast_ref::<ErrorT>()
        {
            if left_error.eq(right_error) {
                if let Some(left_path) = left.attachment_of_type::<PathBuf>()
                    && let Some(right_path) = right.attachment_of_type::<PathBuf>()
                {
                    left_path.eq(right_path)
                } else {
                    true
                }
            } else {
                false
            }
        } else {
            false
        }
    }
}

fn read_files<ProblemReceiverT>(
    paths: &[&str],
    problems: &mut ProblemReceiverT,
) -> Result<Vec<String>, Problem>
where
    ProblemReceiverT: ProblemReceiver,
{
    let mut strings = Vec::default();
    for path in paths {
        if let Some(string) = std::fs::read_to_string(path)
            .via(FileError::new("read_to_string"))
            .with(PathBuf::from(path))
            .with(PathCauseEquality::new::<FileError>())
            .give_ok(problems)?
        {
            strings.push(string);
        }
    }
    Ok(strings)
}

fn main() {
    let mut problems = Problems::default();

    read_files(&["non-existing1.txt", "non-existing2.txt"], &mut problems).unwrap();
    read_files(&["non-existing1.txt", "non-existing2.txt"], &mut problems).unwrap();
    read_files(&["non-existing3.txt", "non-existing4.txt"], &mut problems).unwrap();

    println!("All errors:");
    for problem in &problems {
        println!("• {}", problem);
        if let Some(path) = problem.attachment_of_type::<PathBuf>() {
            println!("  {}", path.display());
        }
    }

    // For iter_unique() to do its job we need an CauseEquality or CauseComparison attachment
    println!("\nUnique errors:");
    for problem in problems.iter_unique() {
        println!("• {}", problem);
        if let Some(path) = problem.attachment_of_type::<PathBuf>() {
            println!("  {}", path.display());
        }
    }
}
