crossterm/command.rs
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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
use std::fmt;
use std::io::{self, Write};
use super::error::Result;
/// An interface for a command that performs an action on the terminal.
///
/// Crossterm provides a set of commands,
/// and there is no immediate reason to implement a command yourself.
/// In order to understand how to use and execute commands,
/// it is recommended that you take a look at [Command API](./index.html#command-api) chapter.
pub trait Command {
/// Write an ANSI representation of this command to the given writer.
/// An ANSI code can manipulate the terminal by writing it to the terminal buffer.
/// However, only Windows 10 and UNIX systems support this.
///
/// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result;
/// Execute this command.
///
/// Windows versions lower than windows 10 do not support ANSI escape codes,
/// therefore a direct WinAPI call is made.
///
/// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()>;
/// Returns whether the ANSI code representation of this command is supported by windows.
///
/// A list of supported ANSI escape codes
/// can be found [here](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
#[cfg(windows)]
fn is_ansi_code_supported(&self) -> bool {
super::ansi_support::supports_ansi()
}
}
impl<T: Command + ?Sized> Command for &T {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
(**self).write_ansi(f)
}
#[inline]
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
T::execute_winapi(self)
}
#[cfg(windows)]
#[inline]
fn is_ansi_code_supported(&self) -> bool {
T::is_ansi_code_supported(self)
}
}
/// An interface for types that can queue commands for further execution.
pub trait QueueableCommand {
/// Queues the given command for further execution.
fn queue(&mut self, command: impl Command) -> Result<&mut Self>;
}
/// An interface for types that can directly execute commands.
pub trait ExecutableCommand {
/// Executes the given command directly.
fn execute(&mut self, command: impl Command) -> Result<&mut Self>;
}
impl<T: Write + ?Sized> QueueableCommand for T {
/// Queues the given command for further execution.
///
/// Queued commands will be executed in the following cases:
///
/// * When `flush` is called manually on the given type implementing `io::Write`.
/// * The terminal will `flush` automatically if the buffer is full.
/// * Each line is flushed in case of `stdout`, because it is line buffered.
///
/// # Arguments
///
/// - [Command](./trait.Command.html)
///
/// The command that you want to queue for later execution.
///
/// # Examples
///
/// ```rust
/// use std::io::{Write, stdout};
///
/// use crossterm::{Result, QueueableCommand, style::Print};
///
/// fn main() -> Result<()> {
/// let mut stdout = stdout();
///
/// // `Print` will executed executed when `flush` is called.
/// stdout
/// .queue(Print("foo 1\n".to_string()))?
/// .queue(Print("foo 2".to_string()))?;
///
/// // some other code (no execution happening here) ...
///
/// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed.
/// stdout.flush()?;
///
/// Ok(())
///
/// // ==== Output ====
/// // foo 1
/// // foo 2
/// }
/// ```
///
/// Have a look over at the [Command API](./index.html#command-api) for more details.
///
/// # Notes
///
/// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
/// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
/// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
/// and can therefore not be written to the given `writer`.
/// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
/// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
fn queue(&mut self, command: impl Command) -> Result<&mut Self> {
#[cfg(windows)]
if !command.is_ansi_code_supported() {
// There may be queued commands in this writer, but `execute_winapi` will execute the
// command immediately. To prevent commands being executed out of order we flush the
// writer now.
self.flush()?;
command.execute_winapi()?;
return Ok(self);
}
write_command_ansi(self, command)?;
Ok(self)
}
}
impl<T: Write + ?Sized> ExecutableCommand for T {
/// Executes the given command directly.
///
/// The given command its ANSI escape code will be written and flushed onto `Self`.
///
/// # Arguments
///
/// - [Command](./trait.Command.html)
///
/// The command that you want to execute directly.
///
/// # Example
///
/// ```rust
/// use std::io::{Write, stdout};
///
/// use crossterm::{Result, ExecutableCommand, style::Print};
///
/// fn main() -> Result<()> {
/// // will be executed directly
/// stdout()
/// .execute(Print("sum:\n".to_string()))?
/// .execute(Print(format!("1 + 1= {} ", 1 + 1)))?;
///
/// Ok(())
///
/// // ==== Output ====
/// // sum:
/// // 1 + 1 = 2
/// }
/// ```
///
/// Have a look over at the [Command API](./index.html#command-api) for more details.
///
/// # Notes
///
/// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
/// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
/// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
/// and can therefore not be written to the given `writer`.
/// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
/// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
fn execute(&mut self, command: impl Command) -> Result<&mut Self> {
self.queue(command)?;
self.flush()?;
Ok(self)
}
}
/// Writes the ANSI representation of a command to the given writer.
fn write_command_ansi<C: Command>(
io: &mut (impl io::Write + ?Sized),
command: C,
) -> io::Result<()> {
struct Adapter<T> {
inner: T,
res: io::Result<()>,
}
impl<T: Write> fmt::Write for Adapter<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.inner.write_all(s.as_bytes()).map_err(|e| {
self.res = Err(e);
fmt::Error
})
}
}
let mut adapter = Adapter {
inner: io,
res: Ok(()),
};
command
.write_ansi(&mut adapter)
.map_err(|fmt::Error| match adapter.res {
Ok(()) => panic!(
"<{}>::write_ansi incorrectly errored",
std::any::type_name::<C>()
),
Err(e) => e,
})
}
/// Executes the ANSI representation of a command, using the given `fmt::Write`.
pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result {
#[cfg(windows)]
if !command.is_ansi_code_supported() {
return command.execute_winapi().map_err(|_| fmt::Error);
}
command.write_ansi(f)
}