Command API improvements: don’t ignore errors, don’t require reassigning stdout back to itself (#226)

This commit is contained in:
Anders Kaseorg 2019-09-18 06:29:02 -07:00 committed by Timon
parent 1b307c2010
commit e5ed617a7b
6 changed files with 54 additions and 45 deletions

View File

@ -33,7 +33,7 @@ impl WinApiColor {
impl ITerminalColor for WinApiColor {
fn set_fg(&self, fg_color: Color) -> Result<()> {
// init the original color in case it is not set.
let _ = init_console_color()?;
init_console_color()?;
let color_value = color_value(Colored::Fg(fg_color));
@ -60,7 +60,7 @@ impl ITerminalColor for WinApiColor {
fn set_bg(&self, bg_color: Color) -> Result<()> {
// init the original color in case it is not set.
let _ = init_console_color()?;
init_console_color()?;
let color_value = color_value(Colored::Bg(bg_color));

View File

@ -266,7 +266,7 @@ pub fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u
fn clear(start_location: Coord, cells_to_write: u32, current_attribute: u16) -> Result<()> {
let console = Console::from(Handle::current_out_handle()?);
let _ = console.fill_whit_character(start_location, cells_to_write, ' ')?;
console.fill_whit_character(start_location, cells_to_write, ' ')?;
console.fill_whit_attribute(start_location, cells_to_write, current_attribute)?;
Ok(())

View File

@ -1,7 +1,6 @@
use std::fmt::Display;
use std::io::Write;
#[cfg(windows)]
use crate::Result;
use crate::{execute, impl_display, queue, write_cout};
@ -32,15 +31,15 @@ pub trait Command {
/// A trait that defines behaviour for a command that can be used to be executed at a later time point.
/// This can be used in order to get more performance.
pub trait QueueableCommand<T: Display> {
pub trait QueueableCommand<T: Display>: Sized {
/// Queues the given command for later execution.
fn queue(self, command: impl Command<AnsiType = T>) -> Self;
fn queue(&mut self, command: impl Command<AnsiType = T>) -> Result<&mut Self>;
}
/// A trait that defines behaviour for a command that will be executed immediately.
pub trait ExecutableCommand<T: Display> {
pub trait ExecutableCommand<T: Display>: Sized {
/// Execute the given command directly.
fn execute(self, command: impl Command<AnsiType = T>) -> Self;
fn execute(&mut self, command: impl Command<AnsiType = T>) -> Result<&mut Self>;
}
impl<T, A> QueueableCommand<A> for T
@ -68,9 +67,9 @@ where
/// This is happening because windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer.
/// Because of that there is no difference between `execute` and `queue` for those windows versions.
/// - Queuing might sound that there is some scheduling going on, however, this means that we write to the stdout without flushing which will cause commands to be stored in the buffer without them being written to the terminal.
fn queue(mut self, command: impl Command<AnsiType = A>) -> Self {
let _ = queue!(self, command);
self
fn queue(&mut self, command: impl Command<AnsiType = A>) -> Result<&mut Self> {
queue!(self, command)?;
Ok(self)
}
}
@ -92,9 +91,9 @@ where
/// - In case of Windows versions lower than 10, a direct WinApi call will be made.
/// This is happening because Windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer.
/// Because of that there is no difference between `execute` and `queue` for those windows versions.
fn execute(mut self, command: impl Command<AnsiType = A>) -> Self {
let _ = execute!(self, command);
self
fn execute(&mut self, command: impl Command<AnsiType = A>) -> Result<&mut Self> {
execute!(self, command)?;
Ok(self)
}
}

View File

@ -76,7 +76,7 @@ By doing this you can make efficient use of the terminal buffer and get better p
```rust
let mut stdout = stdout();
stdout = stdout.queue(Goto(5,5));
stdout.queue(Goto(5,5))?;
// some other code ...
@ -107,10 +107,10 @@ This is fine if you are not executing lots of commands.
_functions_
```rust
stdout().execute(Goto(5,5));
stdout().execute(Goto(5,5))?;
```
The `execute` function returns it self, therefore you are able to use this to execute another command
like `stdout.execute(Goto(5,5)).execute(Clear(ClearType::All))`
like `stdout.execute(Goto(5,5))?.execute(Clear(ClearType::All))?`
_macro's_
```rust
@ -130,14 +130,14 @@ use std::io::stdout();
let mut stdout = stdout();
stdout = stdout.execute(Clear(ClearType::All));
stdout.execute(Clear(ClearType::All))?;
for y in 0..40 {
for x in 0..150 {
if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) {
stdout = stdout
.queue(Goto(x,y))
.queue(PrintStyledFont( "█".magenta()));
stdout
.queue(Goto(x,y))?
.queue(PrintStyledFont( "█".magenta()))?;
}
}
stdout.flush();

View File

@ -3,73 +3,81 @@
use std::io::{stdout, Write};
use crossterm::{
execute, queue, Clear, ClearType, ExecutableCommand, Goto, Output, QueueableCommand,
execute, queue, Clear, ClearType, ExecutableCommand, Goto, Output, QueueableCommand, Result,
};
/// execute commands by using normal functions
fn execute_command_directly_using_functions() {
fn execute_command_directly_using_functions() -> Result<()> {
// single command
let _ = stdout().execute(Output("Text1 ".to_string()));
stdout().execute(Output("Text1 ".to_string()))?;
// multiple commands
let _ = stdout()
.execute(Output("Text2 ".to_string()))
.execute(Output("Text3 ".to_string()));
stdout()
.execute(Output("Text2 ".to_string()))?
.execute(Output("Text3 ".to_string()))?;
Ok(())
}
/// execute commands by using macro's
fn execute_command_directly_using_macros() {
fn execute_command_directly_using_macros() -> Result<()> {
// single command
let _ = execute!(stdout(), Output("Text1 ".to_string()));
execute!(stdout(), Output("Text1 ".to_string()))?;
// multiple commands
let _ = execute!(
execute!(
stdout(),
Output("Text2 ".to_string()),
Output("Text 3".to_string())
);
)?;
Ok(())
}
/// queue commands without executing them directly by using normal functions
fn later_execution_command_using_functions() {
fn later_execution_command_using_functions() -> Result<()> {
let mut sdout = stdout();
// single command
sdout = sdout.queue(Output("Text1 ".to_string()));
sdout.queue(Output("Text1 ".to_string()))?;
// multiple commands
sdout = sdout
.queue(Clear(ClearType::All))
.queue(Goto(5, 5))
sdout
.queue(Clear(ClearType::All))?
.queue(Goto(5, 5))?
.queue(Output(
"console cleared, and moved to coord X: 5 Y: 5 ".to_string(),
));
))?;
::std::thread::sleep(std::time::Duration::from_millis(2000));
// when you call this all commands will be executed
let _ = sdout.flush();
sdout.flush()?;
Ok(())
}
/// queue commands without executing them directly by using macro's
fn later_execution_command_directly_using_macros() {
fn later_execution_command_directly_using_macros() -> Result<()> {
let mut stdout = stdout();
// single command
let _ = queue!(stdout, Output("Text1 ".to_string()));
queue!(stdout, Output("Text1 ".to_string()))?;
// multiple commands
let _ = queue!(
queue!(
stdout,
Clear(ClearType::All),
Goto(5, 5),
Output("console cleared, and moved to coord X: 5 Y: 5 ".to_string())
);
)?;
::std::thread::sleep(std::time::Duration::from_millis(2000));
// when you call this all commands will be executed
let _ = stdout.flush();
stdout.flush()?;
Ok(())
}
// cargo run --example command

View File

@ -135,6 +135,8 @@ pub fn exit() {
}
// cargo run --example terminal
fn main() {
let _ = scroll_down();
fn main() -> io::Result<()> {
scroll_down()?;
Ok(())
}