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
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! A module that contains all the actions related to reading input from the terminal.
//! Like reading a line, reading a character and reading asynchronously.

use std::io;

use crossterm_utils::Result;

#[cfg(unix)]
use super::unix_input::{AsyncReader, SyncReader, UnixInput};
#[cfg(windows)]
use super::windows_input::{AsyncReader, SyncReader, WindowsInput};
use super::ITerminalInput;

/// Allows you to read user input.
///
/// # Features:
///
/// - Read character
/// - Read line
/// - Read async
/// - Read async until
/// - Read sync
/// - Wait for key event (terminal pause)
///
/// Check `/examples/` in the library for more specific examples.
pub struct TerminalInput {
    #[cfg(windows)]
    input: WindowsInput,
    #[cfg(unix)]
    input: UnixInput,
}

impl TerminalInput {
    /// Create a new instance of `TerminalInput` whereon input related actions could be performed.
    pub fn new() -> TerminalInput {
        #[cfg(windows)]
        let input = WindowsInput::new();

        #[cfg(unix)]
        let input = UnixInput::new();

        TerminalInput { input }
    }

    /// Read one line from the user input.
    ///
    /// # Remark
    /// This function is not work when raw screen is turned on.
    /// When you do want to read a line in raw mode please, checkout `read_async`, `read_async_until` or `read_sync`.
    /// Not sure what 'raw mode' is, checkout the 'crossterm_screen' crate.
    ///
    /// # Example
    /// ```ignore
    /// let in = input();
    /// match in.read_line() {
    ///     Ok(s) => println!("string typed: {}", s),
    ///     Err(e) => println!("error: {}", e),
    /// }
    /// ```
    pub fn read_line(&self) -> Result<String> {
        let mut rv = String::new();
        io::stdin().read_line(&mut rv)?;
        let len = rv.trim_end_matches(&['\r', '\n'][..]).len();
        rv.truncate(len);
        Ok(rv)
    }

    /// Read one character from the user input
    ///
    /// ```ignore
    /// let in = input();
    /// match in.read_char() {
    ///     Ok(c) => println!("character pressed: {}", c),
    ///     Err(e) => println!("error: {}", e),
    /// }
    /// ```
    pub fn read_char(&self) -> Result<char> {
        self.input.read_char()
    }

    /// Read the input asynchronously, which means that input events are gathered on the background and will be queued for you to read.
    ///
    /// If you want a blocking, or less resource consuming read to happen use `read_sync()`, this will leave a way all the thread and queueing and will be a blocking read.
    ///
    /// This is the same as `read_async()` but stops reading when a certain character is hit.
    ///
    /// # Remarks
    /// - Readings won't be blocking calls.
    ///   A thread will be fired to read input, on unix systems from TTY and on windows WinApi
    ///   `ReadConsoleW` will be used.
    /// - Input events read from the user will be queued on a MPSC-channel.
    /// - The reading thread will be cleaned up when it drops.
    /// - Requires 'raw screen to be enabled'.
    ///   Not sure what this is? Please checkout the 'crossterm_screen' crate.
    ///
    /// # Examples
    /// Please checkout the example folder in the repository.
    pub fn read_async(&self) -> AsyncReader {
        self.input.read_async()
    }

    /// Read the input asynchronously until a certain delimiter (character as byte) is hit, which means that input events are gathered on the background and will be queued for you to read.
    ///
    /// If you want a blocking or less resource consuming read to happen, use `read_sync()`. This will leave alone the background thread and queues and will be a blocking read.
    ///
    /// This is the same as `read_async()` but stops reading when a certain character is hit.
    ///
    /// # Remarks
    /// - Readings won't be blocking calls.
    ///   A thread will be fired to read input, on unix systems from TTY and on windows WinApi
    ///   `ReadConsoleW` will be used.
    /// - Input events read from the user will be queued on a MPSC-channel.
    /// - The reading thread will be cleaned up when it drops.
    /// - Requires 'raw screen to be enabled'.
    ///   Not sure what this is? Please checkout the 'crossterm_screen' crate.
    ///
    /// # Examples
    /// Please checkout the example folder in the repository.
    pub fn read_until_async(&self, delimiter: u8) -> AsyncReader {
        self.input.read_until_async(delimiter)
    }

    /// Read the input synchronously from the user, which means that reading calls will block.
    /// It also uses less resources than the `AsyncReader` because the background thread and queues are left alone.
    ///
    /// Consider using `read_async` if you don't want the reading call to block your program.
    ///
    /// # Remark
    /// - Readings will be blocking calls.
    ///
    /// # Examples
    /// Please checkout the example folder in the repository.
    pub fn read_sync(&self) -> SyncReader {
        self.input.read_sync()
    }

    /// Enable mouse events to be captured.
    ///
    /// When enabling mouse input, you will be able to capture mouse movements, pressed buttons, and locations.
    ///
    /// # Remark
    /// - Mouse events will be send over the reader created with `read_async`, `read_async_until`, `read_sync`.
    pub fn enable_mouse_mode(&self) -> Result<()> {
        self.input.enable_mouse_mode()
    }

    /// Disable mouse events to be captured.
    ///
    /// When disabling mouse input, you won't be able to capture mouse movements, pressed buttons, and locations anymore.
    pub fn disable_mouse_mode(&self) -> Result<()> {
        self.input.disable_mouse_mode()
    }
}

/// Get a `TerminalInput` instance whereon input related actions can be performed.
pub fn input() -> TerminalInput {
    TerminalInput::new()
}