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}