use {
    problemo::{common::*, *},
    std::thread,
};

// See: examples/accumulate.rs

string_attachment!(Path);

fn read_file(path: String) -> Result<String, Problem> {
    std::fs::read_to_string(&path)
        .into_problem()
        .with(Path::new(path))
        .via(LowLevelError)
}

fn read_files<ProblemReceiverT>(
    paths: Vec<String>,
    problems: &mut ProblemReceiverT,
) -> Result<Vec<String>, Problem>
where
    ProblemReceiverT: ProblemReceiver,
{
    let mut handles = Vec::default();
    for path in paths {
        handles.push(thread::spawn(move || read_file(path)));
    }

    let mut strings = Vec::default();

    for handle in handles {
        match handle.join().expect("join") {
            Ok(string) => strings.push(string),

            // Note that fail fast will quit the loop --
            // which is a good thing because it will drop the remaining
            // handles in the vector
            //
            // A better solution would be to cancel the remaining threads
            // (demonstrating this is beyond the scope of this example)
            Err(problem) => problems.give(problem)?,
        }
    }

    Ok(strings)
}

fn main() -> Result<(), Problem> {
    println!("accumulate:");

    let mut problems = Problems::default();
    let strings = read_files(
        vec![
            "non-existing1.txt".into(),
            "non-existing2.txt".into(),
            "non-existing3.txt".into(),
        ],
        &mut problems,
    )?;

    println!("{} strings", strings.len());

    println!("\nproblems:");

    for problem in problems {
        print_problem(&problem);
    }

    println!("\nfail fast:");

    match read_files(
        vec![
            "non-existing4.txt".into(),
            "non-existing5.txt".into(),
            "non-existing6.txt".into(),
        ],
        &mut FailFast,
    ) {
        Ok(strings) => println!("{} strings", strings.len()),
        Err(problem) => print_problem(&problem),
    }

    Ok(())
}

fn print_problem(problem: &Problem) {
    if let Some(path) = problem.attachment_of_type::<Path>() {
        println!("• {}\n  {}", path, problem);
    } else {
        println!("• {}", problem);
    }
}
