Skip to main content

repline/
prebaked.rs

1//! Here's a menu I prepared earlier!
2//!
3//! Constructs a [Repline] and repeatedly runs the provided closure on the input strings,
4//! obeying the closure's [Response].
5
6use crate::{error::Error as RlError, repline::Repline};
7use std::{error::Error, io::Stdin};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10/// Control codes for the [prebaked menu](read_and)
11pub enum Response {
12    /// Accept the line, and save it to history
13    Accept,
14    /// Reject the line, and clear the buffer
15    Deny,
16    /// End the loop
17    Break,
18    /// Gather more input and try again
19    Continue,
20}
21
22/// Implements a basic menu loop using an embedded [Repline].
23///
24/// Repeatedly runs the provided closure on the input strings,
25/// obeying the closure's [Response].
26///
27/// Captures and displays all user [Error]s.
28///
29/// # Keybinds
30/// - `Ctrl+C` exits the loop
31/// - `Ctrl+D` clears the input, but *runs the closure* with the old input
32pub fn read_and<F>(color: &str, begin: &str, again: &str, mut f: F) -> Result<(), RlError>
33where F: FnMut(&str) -> Result<Response, Box<dyn Error>> {
34    read_and_mut(color, begin, again, |_, line| f(line))
35}
36
37/// Implements a basic menu loop using an embedded [Repline],
38/// provided to the caller's closure, `f`.
39///
40/// Repeatedly runs the provided closure on the input strings,
41/// obeying the closure's [Response]. The closure may modify the
42/// state of the Repline, including taking additional input.
43///
44/// Captures and displays all user [Error]s.
45///
46/// # Keybinds
47/// - `Ctrl+C` exits the loop
48/// - `Ctrl+D` clears the input, but *runs the closure* with the old input
49pub fn read_and_mut<F>(color: &str, begin: &str, again: &str, mut f: F) -> Result<(), RlError>
50where F: FnMut(&mut Repline<'_, Stdin>, &str) -> Result<Response, Box<dyn Error>> {
51    let mut rl = Repline::new(color, begin, again);
52    loop {
53        let line = match rl.read() {
54            Err(RlError::CtrlC(_)) => break,
55            Err(RlError::CtrlD(line)) => {
56                rl.deny();
57                line
58            }
59            Ok(line) => line,
60            Err(e) => Err(e)?,
61        };
62        print!("\x1b[G\x1b[J");
63        match f(&mut rl, &line) {
64            Ok(Response::Accept) => rl.accept(),
65            Ok(Response::Deny) => rl.deny(),
66            Ok(Response::Break) => break,
67            Ok(Response::Continue) => continue,
68            Err(e) => rl.print_inline(format_args!("    \x1b[91m{e}\x1b[0m"))?,
69        }
70    }
71    Ok(())
72}