#[macro_export]
#[doc(hidden)]
macro_rules! csi {
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
}
#[macro_export]
#[doc(hidden)]
macro_rules! osc {
($( $l:expr ),*) => { concat!("\x1B]", $( $l ),*, "\x1B\\") };
}
#[macro_export]
macro_rules! queue {
($writer:expr $(, $command:expr)* $(,)?) => {{
use ::std::io::Write;
Ok($writer.by_ref())
$(.and_then(|writer| $crate::QueueableCommand::queue(writer, $command)))*
.map(|_| ())
}}
}
#[macro_export]
macro_rules! execute {
($writer:expr $(, $command:expr)* $(,)? ) => {{
use ::std::io::Write;
$crate::queue!($writer $(, $command)*)
.and_then(|()| {
::std::io::Write::flush($writer.by_ref())
})
}}
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_display {
(for $($t:ty),+) => {
$(impl ::std::fmt::Display for $t {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
$crate::command::execute_fmt(f, self)
}
})*
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_from {
($from:path, $to:expr) => {
impl From<$from> for ErrorKind {
fn from(e: $from) -> Self {
$to(e)
}
}
};
}
#[cfg(test)]
mod tests {
use std::io;
use std::str;
#[derive(Default, Debug, Clone)]
struct FakeWrite {
buffer: String,
flushed: bool,
}
impl io::Write for FakeWrite {
fn write(&mut self, content: &[u8]) -> io::Result<usize> {
let content = str::from_utf8(content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.buffer.push_str(content);
self.flushed = false;
Ok(content.len())
}
fn flush(&mut self) -> io::Result<()> {
self.flushed = true;
Ok(())
}
}
#[cfg(not(windows))]
mod unix {
use std::fmt;
use super::FakeWrite;
use crate::command::Command;
pub struct FakeCommand;
impl Command for FakeCommand {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str("cmd")
}
}
#[test]
fn test_queue_one() {
let mut result = FakeWrite::default();
queue!(&mut result, FakeCommand).unwrap();
assert_eq!(&result.buffer, "cmd");
assert!(!result.flushed);
}
#[test]
fn test_queue_many() {
let mut result = FakeWrite::default();
queue!(&mut result, FakeCommand, FakeCommand).unwrap();
assert_eq!(&result.buffer, "cmdcmd");
assert!(!result.flushed);
}
#[test]
fn test_queue_trailing_comma() {
let mut result = FakeWrite::default();
queue!(&mut result, FakeCommand, FakeCommand,).unwrap();
assert_eq!(&result.buffer, "cmdcmd");
assert!(!result.flushed);
}
#[test]
fn test_execute_one() {
let mut result = FakeWrite::default();
execute!(&mut result, FakeCommand).unwrap();
assert_eq!(&result.buffer, "cmd");
assert!(result.flushed);
}
#[test]
fn test_execute_many() {
let mut result = FakeWrite::default();
execute!(&mut result, FakeCommand, FakeCommand).unwrap();
assert_eq!(&result.buffer, "cmdcmd");
assert!(result.flushed);
}
#[test]
fn test_execute_trailing_comma() {
let mut result = FakeWrite::default();
execute!(&mut result, FakeCommand, FakeCommand,).unwrap();
assert_eq!(&result.buffer, "cmdcmd");
assert!(result.flushed);
}
}
#[cfg(windows)]
mod windows {
use std::fmt;
use std::cell::RefCell;
use super::FakeWrite;
use crate::command::Command;
type WindowsEventStream = Vec<&'static str>;
struct FakeCommand<'a> {
stream: RefCell<&'a mut WindowsEventStream>,
value: &'static str,
}
impl<'a> FakeCommand<'a> {
fn new(stream: &'a mut WindowsEventStream, value: &'static str) -> Self {
Self {
value,
stream: RefCell::new(stream),
}
}
}
impl<'a> Command for FakeCommand<'a> {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(self.value)
}
fn execute_winapi(&self) -> std::io::Result<()> {
self.stream.borrow_mut().push(self.value);
Ok(())
}
}
fn test_harness(
stream_result: &[&'static str],
test: impl FnOnce(&mut FakeWrite, &mut WindowsEventStream) -> std::io::Result<()>,
) {
let mut stream = WindowsEventStream::default();
let mut writer = FakeWrite::default();
if let Err(err) = test(&mut writer, &mut stream) {
panic!("Error returned from test function: {:?}", err);
}
const EMPTY_RESULT: [&str; 0] = [];
match (writer.buffer.is_empty(), stream.is_empty()) {
(true, true) if stream_result == EMPTY_RESULT => {}
(true, true) => panic!(
"Neither the event stream nor the writer were populated. Expected {:?}",
stream_result
),
(false, true) => {
let result: String = stream_result.iter().copied().collect();
assert_eq!(result, writer.buffer);
assert_eq!(&stream, &EMPTY_RESULT);
}
(true, false) => {
assert_eq!(stream, stream_result);
assert_eq!(writer.buffer, "");
}
(false, false) => panic!(
"Both the writer and the event stream were written to.\n\
Only one should be used, based on is_ansi_code_supported.\n\
stream: {stream:?}\n\
writer: {writer:?}",
stream = stream,
writer = writer,
),
}
}
#[test]
fn test_queue_one() {
test_harness(&["cmd1"], |writer, stream| {
queue!(writer, FakeCommand::new(stream, "cmd1"))
})
}
#[test]
fn test_queue_some() {
test_harness(&["cmd1", "cmd2"], |writer, stream| {
queue!(
writer,
FakeCommand::new(stream, "cmd1"),
FakeCommand::new(stream, "cmd2"),
)
})
}
#[test]
fn test_many_queues() {
test_harness(&["cmd1", "cmd2", "cmd3"], |writer, stream| {
queue!(writer, FakeCommand::new(stream, "cmd1"))?;
queue!(writer, FakeCommand::new(stream, "cmd2"))?;
queue!(writer, FakeCommand::new(stream, "cmd3"))
})
}
}
}