1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
mod error;

pub use error::*;

/// Query the xterm interface, assuming the terminal is in raw mode
/// (or we would block waiting for a newline).
pub fn query<MS: Into<u64>>(query: &str, timeout_ms: MS) -> Result<String, XQError> {
    // I'll use <const N: usize = 100> as soon as default values for const generics
    // are stabilized. See https://github.com/rust-lang/rust/issues/44580
    const N: usize = 100;
    let mut response = [0; N];
    let n = query_buffer(query, &mut response, timeout_ms.into())?;
    let s = std::str::from_utf8(&response[..n])?;
    Ok(s.to_string())
}

/// Query the xterm interface, assuming the terminal is in raw mode
/// (or we would block waiting for a newline).
///
/// Return the number of bytes read.
#[cfg(unix)]
pub fn query_buffer<MS: Into<u64>>(
    query: &str,
    buffer: &mut [u8],
    timeout_ms: MS,
) -> Result<usize, XQError> {
    use mio::{unix::SourceFd, Events, Interest, Poll, Token};
    use std::{
        fs::File,
        io::{self, Read, Write},
        os::fd::AsRawFd,
    };
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    write!(stdout, "{}", query)?;
    stdout.flush()?;
    let mut stdin = File::open("/dev/tty")?;
    let mut poll = Poll::new()?;
    let mut events = Events::with_capacity(1024);
    let stdin_raw_fd = stdin.as_raw_fd();
    let mut stdin_fd = SourceFd(&stdin_raw_fd); // fancy way to pass the 0 const
    poll.registry()
        .register(&mut stdin_fd, Token(0), Interest::READABLE)?;
    let timeout = std::time::Duration::from_millis(timeout_ms.into());
    poll.poll(&mut events, Some(timeout))?;
    for event in &events {
        if event.token() == Token(0) {
            let bytes_written = stdin.read(buffer)?;
            return Ok(bytes_written);
        }
    }
    Err(XQError::Timeout) // no file descriptor was ready in time
}

#[cfg(not(unix))]
pub fn query_buffer(_query: &str, _buffer: &mut [u8], _timeout_ms: u64) -> Result<usize, XQError> {
    Err(XQError::Unsupported)
}