1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Escape Sequence for the terminal.
//!
//! There are a variety of escape sequences (see [ANSI escape
//! codes](http://ascii-table.com/ansi-escape-sequences.php)) for the
//! terminal but we currently have only implemented "Set Graphics
//! Mode". More can be added to the enum as needed.
//!
//! # Examples
//!
//! ```
//! # #[macro_use]
//! # use dinglebit_terminal::escape_sequence::EscapeSequence;
//! # use dinglebit_terminal::consts::*;
//! # fn main() {
//!     let s = EscapeSequence::SetGraphicsMode(vec![OP_BOLD, FG_BLACK, BG_WHITE]).to_string();
//!     assert_eq!(s, "\x1b[1;30;47m");
//! # }
//! ```

use std::fmt;

/// An escape sequence for the terminal.
pub enum EscapeSequence {
    /// Change the foreground, background, and attributes of terminal
    /// text using the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use]
    /// # use dinglebit_terminal::escape_sequence::EscapeSequence;
    /// # use dinglebit_terminal::consts::*;
    /// # fn main() {
    ///     let s = EscapeSequence::SetGraphicsMode(vec![OP_BOLD, FG_BLACK, BG_WHITE]).to_string();
    ///     assert_eq!(s, "\x1b[1;30;47m");
    /// # }
    /// ```
    SetGraphicsMode(Vec<u8>),
}

impl fmt::Display for EscapeSequence {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            EscapeSequence::SetGraphicsMode(v) => {
                // This seems "faster" per an article I found, but we
                // could also just map/collect/join to a string.
                let mut s = String::with_capacity(100);
                if v.len() > 0 {
                    s.push_str(v[0].to_string().as_str());
                    for i in v[1..].into_iter() {
                        s.push_str(";");
                        s.push_str(i.to_string().as_str());
                    }
                }
                write!(f, "{}", format!("\x1b[{}m", s))
            }
        }
    }
}

/// Create a reset escape sequence for the graphics mode.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # use dinglebit_terminal::reset;
/// # fn main() {
///     let s = reset!().to_string();
///     assert_eq!(s, "\x1b[0m");
/// # }
/// ```
#[macro_export]
macro_rules! reset {
    () => {
        $crate::escape_sequence::EscapeSequence::SetGraphicsMode(vec![$crate::consts::OP_RESET])
    };
}

/// Create a SetGraphicsMode using the given vector.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # use dinglebit_terminal::sgm;
/// # use dinglebit_terminal::consts::*;
/// # fn main() {
///     let s = sgm!(vec![OP_BOLD, FG_BLACK, BG_WHITE]).to_string();
///     assert_eq!(s, "\x1b[1;30;47m");
/// # }
/// ```
#[macro_export]
macro_rules! sgm {
    ($e:expr) => {
        $crate::escape_sequence::EscapeSequence::SetGraphicsMode(($e))
    };
}

#[cfg(test)]
mod tests {
    pub use super::*;
    pub use crate::consts::*;

    #[test]
    fn display() {
        let s = sgm!(vec![OP_BOLD, FG_BLACK, BG_WHITE]);
        assert_eq!("test: \x1b[1;30;47m", format!("test: {}", s));
    }
}