ansi_to/
lib.rs

1use std::str::Chars;
2
3struct Lexer<'a> {
4    chars: Chars<'a>,
5}
6
7impl<'a> Lexer<'a> {
8    fn new(s: &'a str) -> Self {
9        let chars = s.chars();
10        Lexer { chars }
11    }
12}
13
14#[derive(Debug, Clone)]
15pub enum Token {
16    Char(char),
17    Color(u32),
18    Bold,
19    Reset,
20    Up(u32),
21    Down(u32),
22    Left(u32),
23    Right(u32),
24}
25
26impl<'a> Iterator for Lexer<'a> {
27    type Item = Token;
28
29    fn next(&mut self) -> Option<Self::Item> {
30        let c = self.chars.next()?;
31        if c.is_ascii_control() && c != '\n' {
32            match self.chars.next()? {
33                '[' => {
34                    let mut num = 0;
35                    let t = loop {
36                        let n = self.chars.next()?;
37                        if n.is_numeric() {
38                            num = num * 10 + (n.to_digit(10)?);
39                        } else {
40                            break n;
41                        }
42                    };
43
44                    match c {
45                        '\x1b' => match t {
46                            'm' | 'M' => {
47                                if num == 0 {
48                                    return Some(Token::Reset);
49                                }
50                                if num == 1 {
51                                    return Some(Token::Bold);
52                                }
53                                Some(Token::Color(num))
54                            }
55                            'a' | 'A' => Some(Token::Up(num)),
56                            'b' | 'B' => Some(Token::Down(num)),
57                            'c' | 'C' => Some(Token::Right(num)),
58                            'd' | 'D' => Some(Token::Left(num)),
59                            _ => {
60                                self.next()
61                            }
62                        },
63                        _ => {
64                            todo!()
65                        }
66                    }
67                }
68                '\n' => self.next(),
69                _ => {
70                    todo!()
71                }
72            }
73        } else {
74            Some(Token::Char(c))
75        }
76    }
77}
78
79#[derive(Debug, Clone)]
80pub struct AnsiColor(pub u32);
81
82impl AnsiColor {
83    pub fn new(c: u32) -> Self {
84        AnsiColor(c)
85    }
86
87    pub fn to_rgb(&self) -> &'static str {
88        match self.0 {
89            30 | 40 => "rgb(0,0,0)",
90            31 | 41 => "rgb(205, 49, 49)",
91            32 | 42 => "rgb(13, 188, 121)",
92            33 | 43 => "rgb(229, 229, 16)",
93            34 | 44 => "rgb(36, 114, 200)",
94            35 | 45 => "rgb(188, 63, 188)",
95            36 | 46 => "rgb(17, 168, 205)",
96            37 | 47 => "rgb(229, 229, 229)",
97
98            90 | 100 => "rgb(102, 102, 102)",
99            91 | 101 => "rgb(241, 76, 76)",
100            92 | 102 => "rgb(35, 209, 139)",
101            93 | 103 => "rgb(245, 245, 67)",
102            94 | 104 => "rgb(59, 142, 234)",
103            95 | 105 => "rgb(214, 112, 214)",
104            96 | 106 => "rgb(41, 184, 219)",
105            97 | 107 => "rgb(229, 229, 229)",
106            _ => "white",
107        }
108    }
109}
110
111#[derive(Debug, Clone)]
112pub struct Node {
113    pub bg_color: AnsiColor,
114    pub color: AnsiColor,
115    pub bold: bool,
116    pub char: char,
117}
118
119#[derive(Debug, Clone)]
120pub struct Canvas {
121    pub pixels: Vec<Vec<Node>>,
122    pub w: usize,
123    pub h: usize,
124}
125
126fn set_node(v: &mut Vec<Vec<Node>>, node: Node, x: usize, y: usize) {
127    while y >= v.len() {
128        v.push(Vec::new());
129    }
130
131    let row = &mut v[y];
132    while x >= row.len() {
133        let mut empty = node.clone();
134        empty.char = ' ';
135        row.push(empty);
136    }
137
138    row[x] = node;
139}
140
141impl Canvas {
142    pub fn new(s: &str) -> Self {
143        let lex = Lexer::new(s);
144
145        let mut cur_x = 0;
146        let mut cur_y = 0;
147        let mut cur_c = 0;
148        let mut cur_bg_c = 0;
149        let mut bold = false;
150        let mut w = 0;
151        let mut h = 0;
152        let mut pixels = Vec::new();
153
154        for i in lex {
155            match i {
156                Token::Char(c) => {
157                    if c == '\n' {
158                        cur_y += 1;
159                        cur_x = 0;
160                    } else {
161                        let node = Node {
162                            char: c,
163                            bg_color: AnsiColor::new(cur_bg_c),
164                            color: AnsiColor::new(cur_c),
165                            bold,
166                        };
167                        set_node(&mut pixels, node, cur_x, cur_y);
168                        cur_x += 1;
169                    }
170
171                    w = w.max(cur_x + 1);
172                    h = h.max(cur_y + 1);
173                }
174                Token::Color(c) => {
175                    if (40..=47).contains(&c) | (100..=107).contains(&c) {
176                        cur_bg_c = c
177                    } else {
178                        cur_c = c
179                    }
180                }
181                Token::Bold => bold = true,
182                Token::Reset => {
183                    bold = false;
184                    cur_c = 0;
185                    cur_bg_c = 0;
186                }
187                Token::Up(c) => {
188                    if cur_y > c as usize {
189                        cur_y -= c as usize
190                    } else {
191                        cur_y = 0;
192                    }
193                }
194                Token::Down(c) => {
195                    cur_y += c as usize;
196                }
197                Token::Left(c) => {
198                    if cur_x > c as usize {
199                        cur_x -= c as usize
200                    } else {
201                        cur_x = 0;
202                    }
203                }
204                Token::Right(c) => {
205                    cur_x += c as usize;
206                }
207            }
208        }
209
210        Canvas { pixels, w, h }
211    }
212}