1use nom;
16use smallvec::SmallVec;
17use CSI;
18
19#[derive(Eq, PartialEq, Copy, Clone, Debug)]
20pub enum SGR {
21 Reset,
22 Font(Weight),
23 Italic(bool),
24 Underline(bool),
25 Blink(bool),
26 Reverse(bool),
27 Invisible(bool),
28 Struck(bool),
29 Foreground(Color),
30 Background(Color),
31}
32
33use self::SGR::*;
34
35#[derive(Eq, PartialEq, Copy, Clone, Debug)]
36pub enum Weight {
37 Normal,
38 Bold,
39 Faint,
40}
41
42#[derive(Eq, PartialEq, Copy, Clone, Debug)]
43pub enum Color {
44 Default,
45 Transparent,
46 Index(u8),
47 Cmy(u8, u8, u8),
48 Cmyk(u8, u8, u8, u8),
49 Rgb(u8, u8, u8),
50}
51
52impl Into<SmallVec<[u32; CSI::SIZE]>> for SGR {
53 #[inline]
54 fn into(self) -> SmallVec<[u32; CSI::SIZE]> {
55 match self {
56 Reset =>
57 small_vec![0],
58
59 Font(Weight::Bold) =>
60 small_vec![1],
61
62 Font(Weight::Faint) =>
63 small_vec![2],
64
65 Italic(true) =>
66 small_vec![3],
67
68 Underline(true) =>
69 small_vec![4],
70
71 Blink(true) =>
72 small_vec![5],
73
74 Reverse(true) =>
75 small_vec![7],
76
77 Invisible(true) =>
78 small_vec![8],
79
80 Struck(true) =>
81 small_vec![9],
82
83 Font(Weight::Normal) =>
84 small_vec![22],
85
86 Italic(false) =>
87 small_vec![23],
88
89 Underline(false) =>
90 small_vec![24],
91
92 Blink(false) =>
93 small_vec![25],
94
95 Reverse(false) =>
96 small_vec![27],
97
98 Invisible(false) =>
99 small_vec![28],
100
101 Struck(false) =>
102 small_vec![29],
103
104 Foreground(Color::Index(index)) if index < 8 =>
105 small_vec![index as u32 + 30],
106
107 Foreground(Color::Index(index)) if index < 16 =>
108 small_vec![index as u32 - 8 + 90],
109
110 Foreground(Color::Index(index)) =>
111 small_vec![38, 5, index as u32],
112
113 Foreground(Color::Default) =>
114 small_vec![38, 0],
115
116 Foreground(Color::Transparent) =>
117 small_vec![38, 1],
118
119 Foreground(Color::Rgb(r, g, b)) =>
120 small_vec![38, 2, r as u32, g as u32, b as u32],
121
122 Foreground(Color::Cmy(c, m, y)) =>
123 small_vec![38, 3, c as u32, m as u32, y as u32],
124
125 Foreground(Color::Cmyk(c, m, y, k)) =>
126 small_vec![38, 4, c as u32, m as u32, y as u32, k as u32],
127
128 Background(Color::Index(index)) if index < 8 =>
129 small_vec![index as u32 + 40],
130
131 Background(Color::Index(index)) if index < 16 =>
132 small_vec![index as u32 - 8 + 100],
133
134 Background(Color::Index(index)) =>
135 small_vec![48, 5, index as u32],
136
137 Background(Color::Default) =>
138 small_vec![48, 0],
139
140 Background(Color::Transparent) =>
141 small_vec![48, 1],
142
143 Background(Color::Rgb(r, g, b)) =>
144 small_vec![48, 2, r as u32, g as u32, b as u32],
145
146 Background(Color::Cmy(c, m, y)) =>
147 small_vec![48, 3, c as u32, m as u32, y as u32],
148
149 Background(Color::Cmyk(c, m, y, k)) =>
150 small_vec![48, 4, c as u32, m as u32, y as u32, k as u32],
151 }
152 }
153}
154
155macro_rules! pop {
156 ($args:ident, $n:expr) => ({
157 let count = $n;
158
159 if $args.len() >= count {
160 $args = &$args[count..];
161 }
162 else {
163 $args = &[];
164 }
165 });
166}
167
168macro_rules! color {
169 ($args:ident) => ({
170 let id = arg!($args[0] => 0);
171 pop!($args, 1);
172
173 match id {
174 0 =>
175 Color::Default,
176
177 1 =>
178 Color::Transparent,
179
180 2 => {
181 let r = arg!($args[0] => 0) as u8;
182 let g = arg!($args[1] => 0) as u8;
183 let b = arg!($args[2] => 0) as u8;
184 pop!($args, 3);
185
186 Color::Rgb(r, g, b)
187 }
188
189 3 => {
190 let c = arg!($args[0] => 0) as u8;
191 let m = arg!($args[1] => 0) as u8;
192 let y = arg!($args[2] => 0) as u8;
193 pop!($args, 3);
194
195 Color::Cmy(c, m, y)
196 }
197
198 4 => {
199 let c = arg!($args[0] => 0) as u8;
200 let m = arg!($args[1] => 0) as u8;
201 let y = arg!($args[2] => 0) as u8;
202 let k = arg!($args[3] => 0) as u8;
203 pop!($args, 4);
204
205 Color::Cmyk(c, m, y, k)
206 }
207
208 5 => {
209 let index = arg!($args[0] => 0) as u8;
210 pop!($args, 1);
211
212 Color::Index(index)
213 }
214
215 _ =>
216 return Err(nom::ErrorKind::Custom(9006))
217 }
218 })
219}
220
221pub fn parse<'a, 'b>(args: &'b [Option<u32>]) -> Result<SmallVec<[SGR; CSI::SIZE]>, nom::ErrorKind> {
222 if args.is_empty() {
223 return Ok(small_vec![Reset]);
224 }
225
226 let mut result = SmallVec::new();
227 let mut args = args;
228
229 while !args.is_empty() {
230 let id = arg!(args[0] => 0);
231 pop!(args, 1);
232
233 result.push(match id {
234 0 =>
235 Reset,
236
237 1 =>
238 Font(Weight::Bold),
239
240 2 =>
241 Font(Weight::Faint),
242
243 3 =>
244 Italic(true),
245
246 4 =>
247 Underline(true),
248
249 5 | 6 =>
250 Blink(true),
251
252 7 =>
253 Reverse(true),
254
255 8 =>
256 Invisible(true),
257
258 9 =>
259 Struck(true),
260
261 22 =>
262 Font(Weight::Normal),
263
264 23 =>
265 Italic(false),
266
267 24 =>
268 Underline(false),
269
270 25 =>
271 Blink(false),
272
273 27 =>
274 Reverse(false),
275
276 28 =>
277 Invisible(false),
278
279 29 =>
280 Struck(false),
281
282 c if c >= 30 && c <= 37 =>
283 Foreground(Color::Index(c as u8 - 30)),
284
285 38 =>
286 Foreground(color!(args)),
287
288 39 =>
289 Foreground(Color::Default),
290
291 c if c >= 40 && c <= 47 =>
292 Background(Color::Index(c as u8 - 40)),
293
294 48 =>
295 Background(color!(args)),
296
297 49 =>
298 Background(Color::Default),
299
300 c if c >= 90 && c <= 97 =>
301 Foreground(Color::Index(c as u8 - 90 + 8)),
302
303 c if c >= 100 && c <= 107 =>
304 Background(Color::Index(c as u8 - 100 + 8)),
305
306 _ =>
307 return Err(nom::ErrorKind::Custom(9001))
308 });
309 }
310
311 Ok(result)
312}
313
314pub mod shim {
315 pub use super::SGR as T;
316 pub use super::SGR::*;
317 pub use super::parse;
318 pub use super::{Weight, Color};
319}
320
321#[cfg(test)]
322mod test {
323 mod parse {
324 use {Control, C1, CSI, SGR, parse};
325
326 macro_rules! test {
327 ($string:expr => $($attrs:expr),+) => (
328 assert_eq!(Control::C1(C1::ControlSequence(CSI::SelectGraphicalRendition(small_vec![$($attrs),*]))),
329 parse($string).unwrap().1);
330 );
331 }
332
333 #[test]
334 fn reset() {
335 test!(b"\x1B[0m" =>
336 SGR::Reset);
337
338 test!(b"\x1B[m" =>
339 SGR::Reset);
340 }
341
342 #[test]
343 fn font() {
344 test!(b"\x1B[1m" =>
345 SGR::Font(SGR::Weight::Bold));
346
347 test!(b"\x1B[2m" =>
348 SGR::Font(SGR::Weight::Faint));
349
350 test!(b"\x1B[22m" =>
351 SGR::Font(SGR::Weight::Normal));
352 }
353
354 #[test]
355 fn italic() {
356 test!(b"\x1B[3m" =>
357 SGR::Italic(true));
358
359 test!(b"\x1B[23m" =>
360 SGR::Italic(false));
361 }
362
363 #[test]
364 fn underline() {
365 test!(b"\x1B[4m" =>
366 SGR::Underline(true));
367
368 test!(b"\x1B[24m" =>
369 SGR::Underline(false));
370 }
371
372 #[test]
373 fn blink() {
374 test!(b"\x1B[5m" =>
375 SGR::Blink(true));
376
377 test!(b"\x1B[6m" =>
378 SGR::Blink(true));
379
380 test!(b"\x1B[25m" =>
381 SGR::Blink(false));
382 }
383
384 #[test]
385 fn reverse() {
386 test!(b"\x1B[7m" =>
387 SGR::Reverse(true));
388
389 test!(b"\x1B[27m" =>
390 SGR::Reverse(false));
391 }
392
393 #[test]
394 fn invisible() {
395 test!(b"\x1B[8m" =>
396 SGR::Invisible(true));
397
398 test!(b"\x1B[28m" =>
399 SGR::Invisible(false));
400 }
401
402 #[test]
403 fn struck() {
404 test!(b"\x1B[9m" =>
405 SGR::Struck(true));
406
407 test!(b"\x1B[29m" =>
408 SGR::Struck(false));
409 }
410
411 #[test]
412 fn foreground() {
413 test!(b"\x1B[38;0m" =>
414 SGR::Foreground(SGR::Color::Default));
415
416 test!(b"\x1B[39m" =>
417 SGR::Foreground(SGR::Color::Default));
418
419 test!(b"\x1B[38;1m" =>
420 SGR::Foreground(SGR::Color::Transparent));
421
422 test!(b"\x1B[30m" =>
423 SGR::Foreground(SGR::Color::Index(0)));
424
425 test!(b"\x1B[37m" =>
426 SGR::Foreground(SGR::Color::Index(7)));
427
428 test!(b"\x1B[38;2;255;;127m" =>
429 SGR::Foreground(SGR::Color::Rgb(255, 0, 127)));
430
431 test!(b"\x1B[38;5;235m" =>
432 SGR::Foreground(SGR::Color::Index(235)));
433
434 test!(b"\x1B[90m" =>
435 SGR::Foreground(SGR::Color::Index(8)));
436
437 test!(b"\x1B[97m" =>
438 SGR::Foreground(SGR::Color::Index(15)));
439 }
440
441 #[test]
442 fn background() {
443 test!(b"\x1B[48;0m" =>
444 SGR::Background(SGR::Color::Default));
445
446 test!(b"\x1B[49m" =>
447 SGR::Background(SGR::Color::Default));
448
449 test!(b"\x1B[48;1m" =>
450 SGR::Background(SGR::Color::Transparent));
451
452 test!(b"\x1B[40m" =>
453 SGR::Background(SGR::Color::Index(0)));
454
455 test!(b"\x1B[47m" =>
456 SGR::Background(SGR::Color::Index(7)));
457
458 test!(b"\x1B[48;2;255;;127m" =>
459 SGR::Background(SGR::Color::Rgb(255, 0, 127)));
460
461 test!(b"\x1B[48;5;235m" =>
462 SGR::Background(SGR::Color::Index(235)));
463
464 test!(b"\x1B[100m" =>
465 SGR::Background(SGR::Color::Index(8)));
466
467 test!(b"\x1B[107m" =>
468 SGR::Background(SGR::Color::Index(15)));
469 }
470
471 #[test]
472 fn sequence() {
473 test!(b"\x1B[38;2;0;255;127;48;2;127;255;0m" =>
474 SGR::Foreground(SGR::Color::Rgb(0, 255, 127)),
475 SGR::Background(SGR::Color::Rgb(127, 255, 0)));
476 }
477 }
478
479 mod format {
480 use {Control, C1, CSI, SGR, format, parse};
481
482 macro_rules! test {
483 ($($attr:expr),+) => (
484 let item = Control::C1(C1::ControlSequence(CSI::SelectGraphicalRendition(
485 small_vec![$($attr),*])));
486
487 assert_eq!(item, parse(&format(&item)).unwrap().1);
488 );
489 }
490
491 #[test]
492 fn reset() {
493 test!(SGR::Reset);
494 }
495
496 #[test]
497 fn font() {
498 test!(SGR::Font(SGR::Weight::Bold));
499 test!(SGR::Font(SGR::Weight::Faint));
500 test!(SGR::Font(SGR::Weight::Normal));
501 }
502
503 #[test]
504 fn italic() {
505 test!(SGR::Italic(true));
506 test!(SGR::Italic(false));
507 }
508
509 #[test]
510 fn underline() {
511 test!(SGR::Underline(true));
512 test!(SGR::Underline(false));
513 }
514
515 #[test]
516 fn blink() {
517 test!(SGR::Blink(true));
518 test!(SGR::Blink(false));
519 }
520
521 #[test]
522 fn reverse() {
523 test!(SGR::Reverse(true));
524 test!(SGR::Reverse(false));
525 }
526
527 #[test]
528 fn invisible() {
529 test!(SGR::Invisible(true));
530 test!(SGR::Invisible(false));
531 }
532
533 #[test]
534 fn struck() {
535 test!(SGR::Struck(true));
536 test!(SGR::Struck(false));
537 }
538
539 #[test]
540 fn foreground() {
541 test!(SGR::Foreground(SGR::Color::Default));
542 test!(SGR::Foreground(SGR::Color::Transparent));
543 test!(SGR::Foreground(SGR::Color::Index(0)));
544 test!(SGR::Foreground(SGR::Color::Index(7)));
545 test!(SGR::Foreground(SGR::Color::Rgb(255, 0, 127)));
546 test!(SGR::Foreground(SGR::Color::Index(235)));
547 test!(SGR::Foreground(SGR::Color::Index(8)));
548 test!(SGR::Foreground(SGR::Color::Index(15)));
549 }
550
551 #[test]
552 fn background() {
553 test!(SGR::Background(SGR::Color::Default));
554 test!(SGR::Background(SGR::Color::Transparent));
555 test!(SGR::Background(SGR::Color::Index(0)));
556 test!(SGR::Background(SGR::Color::Index(7)));
557 test!(SGR::Background(SGR::Color::Rgb(255, 0, 127)));
558 test!(SGR::Background(SGR::Color::Index(235)));
559 test!(SGR::Background(SGR::Color::Index(8)));
560 test!(SGR::Background(SGR::Color::Index(15)));
561 }
562
563 #[test]
564 fn sequence() {
565 test!(SGR::Foreground(SGR::Color::Rgb(0, 255, 127)),
566 SGR::Background(SGR::Color::Rgb(127, 255, 0)));
567 }
568 }
569}