cloop 0.1.0

Cloop - Clap in a loop (easily create a shell integrated with Clap)
Documentation
use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::io::{self, Write};

pub enum InputResult {
    Input(String),
    Interrupted,
    Eof,
}

pub trait InputReader {
    fn read(&mut self, prompt: &str) -> io::Result<InputResult>;
}

impl<F> InputReader for F
where
    F: FnMut(&str) -> io::Result<InputResult>,
{
    fn read(&mut self, prompt: &str) -> io::Result<InputResult> {
        self(prompt)
    }
}

pub struct StdioInputReader;
impl InputReader for StdioInputReader {
    fn read(&mut self, prompt: &str) -> io::Result<InputResult> {
        let mut input = String::new();
        print!("{}", prompt);
        io::stdout().flush()?;
        io::stdin().read_line(&mut input)?;
        Ok(InputResult::Input(input))
    }
}

impl<H, I> InputReader for Editor<H, I>
where
    H: rustyline::Helper,
    I: rustyline::history::History,
{
    fn read(&mut self, prompt: &str) -> io::Result<InputResult> {
        match self.readline(prompt) {
            Ok(s) => {
                self.add_history_entry(&s)
                    .map_err(convert_rustyline_to_io)?;
                Ok(InputResult::Input(s))
            }
            Err(ReadlineError::Eof) => Ok(InputResult::Eof),
            Err(ReadlineError::Interrupted) => Ok(InputResult::Interrupted),
            Err(e) => Err(convert_rustyline_to_io(e)),
        }
    }
}

fn convert_rustyline_to_io(e: ReadlineError) -> io::Error {
    match e {
        ReadlineError::Io(e) => e,
        #[cfg(unix)]
        ReadlineError::Errno(e) => e.into(),
        ReadlineError::WindowResized => io::Error::new(io::ErrorKind::Other, e),
        e => io::Error::new(io::ErrorKind::Interrupted, e),
    }
}