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 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}