a better coloring crate
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
126
127
128
129
130
use proc_macro2::Literal;
use quote::{ToTokens, TokenStreamExt};
use syn::{parse::Parse, LitStr, Result};

fn name2ansi(name: &str) -> Option<&'static str> {
    Some(match name {
        "black" => "\x1b[0;34;30m",
        "red" => "\x1b[0;34;31m",
        "green" => "\x1b[0;34;32m",
        "yellow" => "\x1b[0;34;33m",
        "blue" => "\x1b[0;34;34m",
        "magenta" => "\x1b[0;34;35m",
        "cyan" => "\x1b[0;34;36m",
        "white" => "\x1b[0;34;37m",
        "default" => "\x1b[0;34;39m",

        "bold_black" => "\x1b[1;34;30m",
        "bold_red" => "\x1b[1;34;31m",
        "bold_green" => "\x1b[1;34;32m",
        "bold_yellow" => "\x1b[1;34;33m",
        "bold_blue" => "\x1b[1;34;34m",
        "bold_magenta" => "\x1b[1;34;35m",
        "bold_cyan" => "\x1b[1;34;36m",
        "bold_white" => "\x1b[1;34;37m",
        "bold_default" => "\x1b[1;34;39m",

        "on_black_bold" => "\x1b[1;34;40m",
        "on_red_bold" => "\x1b[1;34;41m",
        "on_green_bold" => "\x1b[1;34;42m",
        "on_yellow_bold" => "\x1b[1;34;43m",
        "on_blue_bold" => "\x1b[1;34;44m",
        "on_magenta_bold" => "\x1b[1;44;35m",
        "on_cyan_bold" => "\x1b[1;34;46m",
        "on_white_bold" => "\x1b[1;34;47m",
        "on_default_bold" => "\x1b[1;34;49m",

        "on_black" => "\x1b[0;34;40m",
        "on_red" => "\x1b[0;34;41m",
        "on_green" => "\x1b[0;34;42m",
        "on_yellow" => "\x1b[0;34;43m",
        "on_blue" => "\x1b[0;34;44m",
        "on_magenta" => "\x1b[0;44;35m",
        "on_cyan" => "\x1b[0;34;46m",
        "on_white" => "\x1b[0;34;47m",
        "on_default" => "\x1b[0;34;49m",

        "reset" => "\x1b[0m",
        "dim" => "\x1b[2m",
        "italic" => "\x1b[3m",
        "underline" => "\x1b[24m",
        "blinking" => "\x1b[5m",
        "hide" => "\x1b[8m",
        "strike" => "\x1b[9m",
        "bold" => "\x1b[1m",
        _ => return None,
    })
}

pub struct CFStr(String);

impl Parse for CFStr {
    fn parse(stream: syn::parse::ParseStream) -> Result<Self> {
        let input = stream.parse::<LitStr>()?.value();
        let mut chars = input.chars().peekable();
        let mut temp = String::new();
        let mut out = String::new();
        while let Some(ch) = chars.next() {
            match ch {
                '{' => {
                    match chars.next() {
                        Some('{') => {
                            out.push('{');
                            continue;
                        }
                        Some('}') => {
                            out.push('{');
                            out.push('}');
                            continue;
                        }
                        Some(ch) => temp.push(ch),
                        None => return Err(stream.error("unexpected eof")),
                    }
                    for ch in chars.by_ref() {
                        match ch {
                            '}' => {
                                if let Some(a) = name2ansi(&temp) {
                                    out.push_str(a);
                                    temp.clear();
                                    break;
                                } else if let Some((b, a)) = temp.split_once(':') {
                                    if let Some(a) = name2ansi(a) {
                                        out.push_str(name2ansi("reset").unwrap());
                                        out.push_str(a);
                                        out.push('{');
                                        out.push_str(b);
                                        out.push('}');
                                        out.push_str(name2ansi("reset").unwrap());
                                        temp.clear();
                                        break;
                                    }
                                }
                                out.push('{');
                                out.push_str(&temp);
                                out.push('}');
                                temp.clear();
                                break;
                            }
                            t => temp.push(t),
                        }
                    }
                }
                '}' => match chars.next() {
                    Some('}') => {
                        out.push('}');
                        continue;
                    }
                    _ => return Err(stream.error("unexpected text")),
                },
                c => out.push(c),
            }
        }
        Ok(Self(out))
    }
}

impl ToTokens for CFStr {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        tokens.append(Literal::string(&self.0));
    }
}