ttyaskpass/
lib.rs

1mod readtty;
2mod colorhash;
3
4use std::io;
5use bstr::ByteSlice;
6use mortal::{ Event, Key };
7use colorhash::{ ColorStar, hash_chars_as_color, random_color };
8use readtty::Term;
9
10
11pub struct AskPass<B> {
12    star: char,
13    buf: B
14}
15
16impl<B: AsMut<[u8]>> AskPass<B> {
17    pub fn new(buf: B) -> AskPass<B> {
18        AskPass { star: '*', buf }
19    }
20
21    pub fn with_star(mut self, star: char) -> AskPass<B> {
22        self.star = star;
23        self
24    }
25
26    pub fn as_buffer(&self) -> &B {
27        &self.buf
28    }
29
30    pub fn into_buffer(self) -> B {
31        self.buf
32    }
33}
34
35impl<B: AsMut<[u8]>> AskPass<B> {
36    /// AskPass
37    ///
38    /// Note that an error (`io::ErrorKind::Interrupted`) will be returned
39    /// when the user interrupts with `Ctrl-c`.
40    pub fn askpass<'a>(&'a mut self, prompt: &str) -> io::Result<&'a [u8]> {
41        let terminal = Term::new()?;
42        let mut pos = 0;
43
44        terminal.read_event(|event| {
45            let buf = self.buf.as_mut();
46            let star = self.star;
47
48            match event {
49                Event::Key(Key::Enter) => return Ok(true),
50                Event::Key(Key::Char(c)) => if buf.len() - pos > c.len_utf8() {
51                    c.encode_utf8(&mut buf[pos..]);
52                    pos += c.len_utf8();
53                },
54                Event::Key(Key::Backspace) => pos = buf[..pos].char_indices()
55                    .last()
56                    .map(|(start, ..)| start)
57                    .unwrap_or(0)
58                ,
59                Event::Key(Key::Escape) => pos = 0,
60                Event::Key(Key::Ctrl('c')) =>
61                    return Err(io::Error::new(io::ErrorKind::Interrupted, "Ctrl-c")),
62                Event::NoEvent => (),
63                _ => return Ok(false)
64            }
65
66            let colors = match pos {
67                0 => ColorStar::from(star),
68                1..=7 => random_color(star),
69                p => hash_chars_as_color(star, &buf[..p])
70            };
71
72            write!(terminal.inner, "\r{} ", prompt)?;
73            colors.write(&terminal.inner)?;
74
75            Ok(false)
76        })?;
77
78        write!(terminal.inner, "\r")?;
79        terminal.inner.clear_to_line_end()?;
80
81        let buf = self.buf.as_mut();
82        Ok(&buf[..pos])
83    }
84}