crossterm/
command.rs

1use std::fmt;
2use std::io::{self, Write};
3
4use super::error::Result;
5
6/// An interface for a command that performs an action on the terminal.
7///
8/// Crossterm provides a set of commands,
9/// and there is no immediate reason to implement a command yourself.
10/// In order to understand how to use and execute commands,
11/// it is recommended that you take a look at [Command API](./index.html#command-api) chapter.
12pub trait Command {
13    /// Write an ANSI representation of this command to the given writer.
14    /// An ANSI code can manipulate the terminal by writing it to the terminal buffer.
15    /// However, only Windows 10 and UNIX systems support this.
16    ///
17    /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
18    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result;
19
20    /// Execute this command.
21    ///
22    /// Windows versions lower than windows 10 do not support ANSI escape codes,
23    /// therefore a direct WinAPI call is made.
24    ///
25    /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
26    #[cfg(windows)]
27    fn execute_winapi(&self) -> Result<()>;
28
29    /// Returns whether the ANSI code representation of this command is supported by windows.
30    ///
31    /// A list of supported ANSI escape codes
32    /// can be found [here](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
33    #[cfg(windows)]
34    fn is_ansi_code_supported(&self) -> bool {
35        super::ansi_support::supports_ansi()
36    }
37}
38
39impl<T: Command + ?Sized> Command for &T {
40    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
41        (**self).write_ansi(f)
42    }
43
44    #[inline]
45    #[cfg(windows)]
46    fn execute_winapi(&self) -> Result<()> {
47        T::execute_winapi(self)
48    }
49
50    #[cfg(windows)]
51    #[inline]
52    fn is_ansi_code_supported(&self) -> bool {
53        T::is_ansi_code_supported(self)
54    }
55}
56
57/// An interface for types that can queue commands for further execution.
58pub trait QueueableCommand {
59    /// Queues the given command for further execution.
60    fn queue(&mut self, command: impl Command) -> Result<&mut Self>;
61}
62
63/// An interface for types that can directly execute commands.
64pub trait ExecutableCommand {
65    /// Executes the given command directly.
66    fn execute(&mut self, command: impl Command) -> Result<&mut Self>;
67}
68
69impl<T: Write + ?Sized> QueueableCommand for T {
70    /// Queues the given command for further execution.
71    ///
72    /// Queued commands will be executed in the following cases:
73    ///
74    /// * When `flush` is called manually on the given type implementing `io::Write`.
75    /// * The terminal will `flush` automatically if the buffer is full.
76    /// * Each line is flushed in case of `stdout`, because it is line buffered.
77    ///
78    /// # Arguments
79    ///
80    /// - [Command](./trait.Command.html)
81    ///
82    ///     The command that you want to queue for later execution.
83    ///
84    /// # Examples
85    ///
86    /// ```rust
87    /// use std::io::{Write, stdout};
88    ///
89    /// use crossterm::{Result, QueueableCommand, style::Print};
90    ///
91    ///  fn main() -> Result<()> {
92    ///     let mut stdout = stdout();
93    ///
94    ///     // `Print` will executed executed when `flush` is called.
95    ///     stdout
96    ///         .queue(Print("foo 1\n".to_string()))?
97    ///         .queue(Print("foo 2".to_string()))?;
98    ///
99    ///     // some other code (no execution happening here) ...
100    ///
101    ///     // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed.
102    ///     stdout.flush()?;
103    ///
104    ///     Ok(())
105    ///
106    ///     // ==== Output ====
107    ///     // foo 1
108    ///     // foo 2
109    /// }
110    /// ```
111    ///
112    /// Have a look over at the [Command API](./index.html#command-api) for more details.
113    ///
114    /// # Notes
115    ///
116    /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
117    /// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
118    ///     The reason for this is that Windows versions lower than 10 do not support ANSI codes,
119    ///     and can therefore not be written to the given `writer`.
120    ///     Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
121    ///     and [queue](./trait.QueueableCommand.html) for those old Windows versions.
122    fn queue(&mut self, command: impl Command) -> Result<&mut Self> {
123        #[cfg(windows)]
124        if !command.is_ansi_code_supported() {
125            // There may be queued commands in this writer, but `execute_winapi` will execute the
126            // command immediately. To prevent commands being executed out of order we flush the
127            // writer now.
128            self.flush()?;
129            command.execute_winapi()?;
130            return Ok(self);
131        }
132
133        write_command_ansi(self, command)?;
134        Ok(self)
135    }
136}
137
138impl<T: Write + ?Sized> ExecutableCommand for T {
139    /// Executes the given command directly.
140    ///
141    /// The given command its ANSI escape code will be written and flushed onto `Self`.
142    ///
143    /// # Arguments
144    ///
145    /// - [Command](./trait.Command.html)
146    ///
147    ///     The command that you want to execute directly.
148    ///
149    /// # Example
150    ///
151    /// ```rust
152    /// use std::io::{Write, stdout};
153    ///
154    /// use crossterm::{Result, ExecutableCommand, style::Print};
155    ///
156    ///  fn main() -> Result<()> {
157    ///      // will be executed directly
158    ///       stdout()
159    ///         .execute(Print("sum:\n".to_string()))?
160    ///         .execute(Print(format!("1 + 1= {} ", 1 + 1)))?;
161    ///
162    ///       Ok(())
163    ///
164    ///      // ==== Output ====
165    ///      // sum:
166    ///      // 1 + 1 = 2
167    ///  }
168    /// ```
169    ///
170    /// Have a look over at the [Command API](./index.html#command-api) for more details.
171    ///
172    /// # Notes
173    ///
174    /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
175    /// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
176    ///     The reason for this is that Windows versions lower than 10 do not support ANSI codes,
177    ///     and can therefore not be written to the given `writer`.
178    ///     Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
179    ///     and [queue](./trait.QueueableCommand.html) for those old Windows versions.
180    fn execute(&mut self, command: impl Command) -> Result<&mut Self> {
181        self.queue(command)?;
182        self.flush()?;
183        Ok(self)
184    }
185}
186
187/// Writes the ANSI representation of a command to the given writer.
188fn write_command_ansi<C: Command>(
189    io: &mut (impl io::Write + ?Sized),
190    command: C,
191) -> io::Result<()> {
192    struct Adapter<T> {
193        inner: T,
194        res: io::Result<()>,
195    }
196
197    impl<T: Write> fmt::Write for Adapter<T> {
198        fn write_str(&mut self, s: &str) -> fmt::Result {
199            self.inner.write_all(s.as_bytes()).map_err(|e| {
200                self.res = Err(e);
201                fmt::Error
202            })
203        }
204    }
205
206    let mut adapter = Adapter {
207        inner: io,
208        res: Ok(()),
209    };
210
211    command
212        .write_ansi(&mut adapter)
213        .map_err(|fmt::Error| match adapter.res {
214            Ok(()) => panic!(
215                "<{}>::write_ansi incorrectly errored",
216                std::any::type_name::<C>()
217            ),
218            Err(e) => e,
219        })
220}
221
222/// Executes the ANSI representation of a command, using the given `fmt::Write`.
223pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result {
224    #[cfg(windows)]
225    if !command.is_ansi_code_supported() {
226        return command.execute_winapi().map_err(|_| fmt::Error);
227    }
228
229    command.write_ansi(f)
230}