use std::io::Result;
use crate::util::ByteFormat;
use crate::{Input, Output};
pub trait Command: core::fmt::Debug + core::fmt::Display {}
impl<C: Command + ?Sized> Command for &C {}
impl<C: Command + ?Sized> Command for Box<C> {}
#[macro_export]
macro_rules! fuse {
($($command:expr),+ $(,)?) => {{
#[derive(Copy, Clone, PartialEq, Eq)]
struct Fused;
impl $crate::Command for Fused {}
impl ::core::fmt::Debug for Fused {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
f.write_str(concat!(stringify!(fuse!), "(", stringify!($($command),+), ")"))
}
}
impl ::core::fmt::Display for Fused {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
$($command.fmt(f)?;)*
Ok(())
}
}
Fused
}}
}
pub trait Sgr: Command {
fn write_param(&self, out: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result;
}
impl<S: Sgr + ?Sized> Sgr for &S {
fn write_param(&self, out: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
(**self).write_param(out)
}
}
impl<S: Sgr + ?Sized> Sgr for Box<S> {
fn write_param(&self, out: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result {
(**self).write_param(out)
}
}
#[macro_export]
macro_rules! fuse_sgr {
( $sgr:expr, $( $sgr2:expr ),* $(,)? ) => {{
#[derive(Copy, Clone, PartialEq, Eq)]
struct FusedSgr;
impl ::core::fmt::Debug for FusedSgr {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
f.write_str(concat!(stringify!(fuse_sgr!), "(", stringify!($sgr, $($sgr2),*), ")"))
}
}
impl ::core::fmt::Display for FusedSgr {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str("\x1b[")?;
self.write_param(f)?;
f.write_str("m")
}
}
impl $crate::Command for FusedSgr {}
impl $crate::Sgr for FusedSgr {
fn write_param(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
$sgr.write_param(f)?;
$(
f.write_str(";")?;
$sgr2.write_param(f)?;
)*
Ok(())
}
}
FusedSgr
}};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Control {
BEL = 0x07,
ESC = 0x1b,
DCS = 0x90,
SOS = 0x98,
SS2 = 0x8e,
SS3 = 0x8f,
CSI = 0x9b,
ST = 0x9c,
OSC = 0x9d,
PM = 0x9e,
APC = 0x9f,
}
impl core::fmt::Display for Control {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use self::Control::*;
f.write_str(match *self {
BEL => "\x07",
ESC => "\x1b",
DCS => "\x1bP",
SOS => "\x1bX",
SS2 => "\x1bN",
SS3 => "\x1bO",
CSI => "\x1b[",
ST => "\x1b\\",
OSC => "\x1b]",
PM => "\x1b^",
APC => "\x1b_",
})
}
}
pub trait Query: Command {
type Response;
fn control(&self) -> Control;
fn parse(&self, payload: &[u8]) -> Result<Self::Response>;
fn run(&self, input: &mut Input, output: &mut Output) -> Result<Self::Response> {
output.exec(self)?;
let payload = input.read_sequence(self.control())?;
self.parse(payload)
}
}
impl<Q: Query + ?Sized> Query for &Q {
type Response = Q::Response;
fn control(&self) -> Control {
(**self).control()
}
fn parse(&self, payload: &[u8]) -> Result<Self::Response> {
(**self).parse(payload)
}
}
impl<Q: Query + ?Sized> Query for Box<Q> {
type Response = Q::Response;
fn control(&self) -> Control {
(**self).control()
}
fn parse(&self, payload: &[u8]) -> Result<Self::Response> {
(**self).parse(payload)
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum Token<'t> {
Text(&'t [u8]),
Control(&'t [u8]),
Sequence(Control, &'t [u8]),
}
impl Token<'_> {
pub fn control(&self) -> Option<Control> {
match *self {
Token::Sequence(control, _) => Some(control),
_ => None,
}
}
pub fn data(&self) -> &[u8] {
use self::Token::*;
match *self {
Text(data) => data,
Control(data) => data,
Sequence(_, data) => data,
}
}
}
impl core::fmt::Debug for Token<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let name = match *self {
Self::Text(_) => "Text",
Self::Control(_) => "Control",
Self::Sequence(_, _) => "Sequence",
};
let mut debug = f.debug_tuple(name);
if let Some(control) = self.control() {
debug.field(&control);
}
debug
.field(&format!("{}", ByteFormat::Nicely(self.data())))
.finish()
}
}
pub trait Scan: std::io::BufRead {
fn in_flight(&self) -> bool;
fn read_token(&mut self) -> Result<Token<'_>>;
fn read_sequence(&mut self, control: Control) -> Result<&[u8]> {
match self.read_token()? {
Token::Sequence(actual, payload) => {
if actual == control {
Ok(payload)
} else {
Err(crate::err::ErrorKind::BadControl.into())
}
}
_ => Err(crate::err::ErrorKind::NotASequence.into()),
}
}
}
impl<S: Scan + ?Sized> Scan for &mut S {
#[inline]
fn in_flight(&self) -> bool {
(**self).in_flight()
}
#[inline]
fn read_token(&mut self) -> Result<Token<'_>> {
(**self).read_token()
}
}
impl<S: Scan + ?Sized> Scan for Box<S> {
#[inline]
fn in_flight(&self) -> bool {
(**self).in_flight()
}
#[inline]
fn read_token(&mut self) -> Result<Token<'_>> {
(**self).read_token()
}
}
fn _assert_traits_are_object_safe<T>() {
fn is_object_safe<T: ?Sized>() {}
is_object_safe::<dyn Command>();
is_object_safe::<dyn Sgr>();
is_object_safe::<dyn Query<Response = T>>();
is_object_safe::<dyn Scan>();
}
#[cfg(test)]
mod test {
use super::*;
use crate::cmd::{Format, SetBackground8, SetForeground8};
#[test]
fn test_fuse() {
let s = format!(
"{}",
fuse!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>)
);
assert_eq!(s, "\x1b[1m\x1b[30m\x1b[107m");
let cmd = fuse!(Format::Blinking, SetBackground8::<219>);
assert_eq!(format!("{}", cmd), "\x1b[5m\x1b[48;5;219m");
assert_eq!(
format!("{:?}", cmd),
"fuse!(Format::Blinking, SetBackground8::<219>)"
);
let double = format!("{}{}", cmd, cmd);
let copy = cmd;
assert_eq!(format!("{}{}", cmd, copy), double);
assert_eq!(
format!("{:?}", cmd),
"fuse!(Format::Blinking, SetBackground8::<219>)"
);
let clone = cmd.clone();
assert_eq!(format!("{}{}", cmd, clone), double);
assert_eq!(
format!("{:?}", cmd),
"fuse!(Format::Blinking, SetBackground8::<219>)"
);
assert_eq!(cmd, copy);
assert_eq!(cmd, clone);
}
#[test]
fn test_fuse_sgr() {
let s = format!(
"{}",
fuse_sgr!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>)
);
assert_eq!(s, "\x1b[1;30;107m");
let cmd = fuse_sgr!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>);
assert_eq!(format!("{}", cmd), "\x1b[1;30;107m");
assert_eq!(
format!("{:?}", cmd),
"fuse_sgr!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>)"
);
let double = format!("{}{}", cmd, cmd);
let copy = cmd;
assert_eq!(format!("{}{}", cmd, copy), double);
assert_eq!(
format!("{:?}", cmd),
"fuse_sgr!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>)"
);
let clone = cmd.clone();
assert_eq!(format!("{}{}", cmd, clone), double);
assert_eq!(
format!("{:?}", cmd),
"fuse_sgr!(Format::Bold, SetForeground8::<0>, SetBackground8::<15>)"
);
assert_eq!(cmd, copy);
assert_eq!(cmd, clone);
}
}