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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
mod display;
mod ops;

use once_cell::sync::Lazy;
use std::collections::HashMap;

pub static ESCSEQ: Lazy<HashMap<String, HashMap<String, String>>> =
    Lazy::new(|| serde_json::from_str(include_str!("../resources/escseq.json")).unwrap());

#[inline]
pub fn erase_screen() {
    print!("{}", &ESCSEQ["erase"]["screen"]);
}

#[inline]
pub fn reset_cursor() {
    print!("{}", &ESCSEQ["reset"]["cursor"]);
}

pub struct TermString {
    data: String,
    esc_len: usize,
}

impl TermString {
    pub fn wrap<T: ToString>(seq: &T) -> Self {
        TermString {
            data: seq.to_string(),
            esc_len: 0,
        }
    }

    pub fn from_escseq(cat: &str, key: &str) -> Option<Self> {
        let seq = ESCSEQ.get(cat)?.get(key)?;
        Some(TermString {
            data: seq.clone(),
            esc_len: seq.len(),
        })
    }

    pub fn len(&self) -> usize {
        self.data.len() - self.esc_len
    }

    pub fn center(mut self, mut width: usize) -> Self {
        width += self.esc_len;
        self.data = format!("{:^width$}", self.data);
        self
    }

    pub fn ljust(mut self, mut width: usize) -> Self {
        width += self.esc_len;
        self.data = format!("{:<width$}", self.data);
        self
    }

    pub fn rjust(mut self, mut width: usize) -> Self {
        width += self.esc_len;
        self.data = format!("{:>width$}", self.data);
        self
    }

    pub fn set_bold(self) -> Self {
        Self::from_escseq("style", "bold").unwrap()
            + self
            + Self::from_escseq("reset", "bold/dim").unwrap()
    }

    pub fn set_dim(self) -> Self {
        Self::from_escseq("style", "dim").unwrap()
            + self
            + Self::from_escseq("reset", "bold/dim").unwrap()
    }

    pub fn set_bold_dim(self) -> Self {
        Self::from_escseq("style", "bold").unwrap()
            + Self::from_escseq("style", "dim").unwrap()
            + self
            + Self::from_escseq("reset", "bold/dim").unwrap()
    }

    pub fn set_italic(self) -> Self {
        Self::from_escseq("style", "italic").unwrap()
            + self
            + Self::from_escseq("reset", "italic").unwrap()
    }

    pub fn set_underline(self) -> Self {
        Self::from_escseq("style", "underline").unwrap()
            + self
            + Self::from_escseq("reset", "underline").unwrap()
    }

    pub fn set_strikethrough(self) -> Self {
        Self::from_escseq("style", "strikethrough").unwrap()
            + self
            + Self::from_escseq("reset", "strikethrough").unwrap()
    }

    pub fn set_color(self, color: &str) -> Result<Self, &'static str> {
        if let Some(color_seq) = Self::from_escseq("foreground", color) {
            Ok(color_seq + self + Self::from_escseq("reset", "foreground").unwrap())
        } else {
            Err("invalid color")
        }
    }

    pub fn set_color_bg(self, color: &str) -> Result<Self, &'static str> {
        if let Some(color_seq) = Self::from_escseq("background", color) {
            Ok(color_seq + self + Self::from_escseq("reset", "background").unwrap())
        } else {
            Err("invalid color")
        }
    }
}

pub trait ToTermString {
    fn to_tstr(&self) -> TermString;
}

impl<T: ToString> ToTermString for T {
    fn to_tstr(&self) -> TermString {
        TermString::wrap(self)
    }
}