challenge_prompt/lib.rs
1//! `challenge_prompt` makes the user pause before doing something.
2//!
3//! A "challenge" prompt introduces a hurdle the user has to pass before
4//! continuing. This is useful in deployment scripts or in scary commands to
5//! make sure they were not typed by rote or pulled out of a shell history by
6//! mistake.
7//!
8//! Available prompts are:
9//! - arithmetic: Asks the user to solve a problem like `(12 + 7) mod 4`,
10//! - phrase: Asks the user to type in a phrase like "I am probably making a
11//! mistake" exactly,
12//! - yes: Asks the user to type in 'y' or 'yes'.
13//!
14//! This crate is both a library and a small command line application for use in
15//! shell scripts.
16//!
17//! ## Command-line example
18//!
19//! ```ignore
20//! $ cargo install challenge-prompt
21//! $ challenge-prompt
22//! Solve: (5 + 9) mod 6 = ?
23//! ```
24//!
25//! ## Library example
26//!
27//! ```toml
28//! [dependencies]
29//! challenge_prompt = "0.3"
30//! ```
31//!
32//! ```no_run
33//! extern crate challenge_prompt;
34//!
35//! let mut rng = challenge_prompt::Rng::new_from_time().unwrap();
36//! if !challenge_prompt::Challenge::Arithmetic.prompt(&mut rng) {
37//! panic!("user failed the challenge")
38//! }
39//! ```
40
41mod rng;
42
43#[derive(Debug)]
44pub enum Challenge {
45 Arithmetic,
46 Phrase(String),
47 Yes,
48}
49
50pub use rng::{Rng, RngError};
51
52pub const DEFAULT_PHRASE: &str = "I am probably making a mistake.";
53
54impl Challenge {
55 /// Prompt the user with the challenge and return whether they passed or
56 /// not.
57 pub fn prompt(&self, rng: &mut rng::Rng) -> bool {
58 use Challenge::*;
59 match self {
60 Arithmetic => {
61 let a = rng.u32() % 20;
62 let b = rng.u32() % 20;
63 let c = rng.u32() % 18 + 2;
64 prompt_gen(
65 &format!("Solve: ({} + {}) mod {} = ?", a, b, c),
66 &[&format!("{}", (a + b) % c)],
67 )
68 }
69 Phrase(str) => prompt_gen(
70 &format!("Enter the following exactly to continue: {}", str),
71 &[str],
72 ),
73 Yes => prompt_gen("Continue? [y]", &["y", "yes"]),
74 }
75 }
76}
77
78/// Generic prompt implementation: print out the `prompt`, read a line of input,
79/// and return whether it matches `expected` or not.
80pub fn prompt_gen(prompt: &str, expected: &[&str]) -> bool {
81 use std::io;
82 println!("{}", prompt);
83 let mut input = String::new();
84 loop {
85 if io::stdin().read_line(&mut input).is_ok() {
86 let input = input.trim();
87 return expected.iter().any(|&e| e == input);
88 }
89 }
90}