summaryrefslogtreecommitdiff
path: root/src/verify.rs
blob: 020102e51a05a5737bbfcb48bf7f84503a5dfc82 (plain)
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::exercise::{ContextLine, Exercise, Mode, State};
use console::{style, Emoji};
use indicatif::ProgressBar;

pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
    for exercise in start_at {
        let is_done = match exercise.mode {
            Mode::Test => test(&exercise)?,
            Mode::Compile => compile_only(&exercise)?,
        };
        if !is_done {
            return Err(());
        }
    }
    Ok(())
}

fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
    let progress_bar = ProgressBar::new_spinner();
    progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
    progress_bar.enable_steady_tick(100);
    let compile_output = exercise.compile();
    progress_bar.finish_and_clear();
    if compile_output.status.success() {
        let formatstr = format!("{} Successfully compiled {}!", Emoji("✅", "✓"), exercise);
        println!("{}", style(formatstr).green());
        exercise.clean();
        if let State::Pending(context) = exercise.state() {
            print_everything_looks_good(exercise.mode, context);
            Ok(false)
        } else {
            Ok(true)
        }
    } else {
        let formatstr = format!(
            "{} Compilation of {} failed! Compiler error message:\n",
            Emoji("⚠️ ", "!"),
            exercise
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compile_output.stderr));
        exercise.clean();
        Err(())
    }
}

pub fn test(exercise: &Exercise) -> Result<bool, ()> {
    let progress_bar = ProgressBar::new_spinner();
    progress_bar.set_message(format!("Testing {}...", exercise).as_str());
    progress_bar.enable_steady_tick(100);

    let compile_output = exercise.compile();
    if compile_output.status.success() {
        progress_bar.set_message(format!("Running {}...", exercise).as_str());

        let runcmd = exercise.run();
        progress_bar.finish_and_clear();

        if runcmd.status.success() {
            let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
            println!("{}", style(formatstr).green());
            exercise.clean();
            if let State::Pending(context) = exercise.state() {
                print_everything_looks_good(exercise.mode, context);
                Ok(false)
            } else {
                Ok(true)
            }
        } else {
            let formatstr = format!(
                "{} Testing of {} failed! Please try again. Here's the output:",
                Emoji("⚠️ ", "!"),
                exercise
            );
            println!("{}", style(formatstr).red());
            println!("{}", String::from_utf8_lossy(&runcmd.stdout));
            exercise.clean();
            Err(())
        }
    } else {
        progress_bar.finish_and_clear();
        let formatstr = format!(
            "{} Compiling of {} failed! Please try again. Here's the output:",
            Emoji("⚠️ ", "!"),
            exercise
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compile_output.stderr));
        exercise.clean();
        Err(())
    }
}

fn print_everything_looks_good(mode: Mode, context: Vec<ContextLine>) {
    let success_msg = match mode {
        Mode::Compile => "The code is compiling!",
        Mode::Test => "The code is compiling, and the tests pass!",
    };

    println!("");
    println!("🎉 🎉  {} 🎉 🎉", success_msg);
    println!("");
    println!("You can keep working on this exercise,");
    println!(
        "or jump into the next one by removing the {} comment:",
        style("`I AM NOT DONE`").bold()
    );
    println!("");
    for context_line in context {
        let formatted_line = if context_line.important {
            format!("{}", style(context_line.line).bold())
        } else {
            format!("{}", context_line.line)
        };

        println!(
            "{:>2} {}  {}",
            style(context_line.number).blue().bold(),
            style("|").blue(),
            formatted_line
        );
    }
}