colourizer/
lib.rs

1use std::error::Error;
2use std::io::{self, BufRead};
3use rargsxd::*;
4
5const OFF: &'static str = "\x1b[m";
6
7/// Trait with a set of functions of colouring a string.
8pub trait StyledString {
9    /// Every other function uses this to access the string itself.
10    fn get_str(&self) -> String;
11
12    fn do_off(&self) -> &str {
13        if self.get_str().ends_with(OFF) {""}
14        else {OFF}
15    }
16
17    fn remove_off(&self) -> String {
18        let mut cur = self.get_str();
19        while let Some(new) = cur.strip_suffix(OFF) {
20            cur = new.to_string();
21        }
22        cur
23    }
24
25    fn fgblack(&self) -> String     {format!("\x1b[30m{}{}", self.get_str(), self.do_off())}
26    fn fgred(&self) -> String       {format!("\x1b[31m{}{}", self.get_str(), self.do_off())}
27    fn fggreen(&self) -> String     {format!("\x1b[32m{}{}", self.get_str(), self.do_off())}
28    fn fgyellow(&self) -> String    {format!("\x1b[33m{}{}", self.get_str(), self.do_off())}
29    fn fgblue(&self) -> String      {format!("\x1b[34m{}{}", self.get_str(), self.do_off())}
30    fn fgmagenta(&self) -> String   {format!("\x1b[35m{}{}", self.get_str(), self.do_off())}
31    fn fgcyan(&self) -> String      {format!("\x1b[36m{}{}", self.get_str(), self.do_off())}
32    fn fgwhite(&self) -> String     {format!("\x1b[37m{}{}", self.get_str(), self.do_off())}
33
34    fn bgblack(&self) -> String     {format!("\x1b[40m{}{}", self.get_str(), self.do_off())}
35    fn bgred(&self) -> String       {format!("\x1b[41m{}{}", self.get_str(), self.do_off())}
36    fn bggreen(&self) -> String     {format!("\x1b[42m{}{}", self.get_str(), self.do_off())}
37    fn bgyellow(&self) -> String    {format!("\x1b[43m{}{}", self.get_str(), self.do_off())}
38    fn bgblue(&self) -> String      {format!("\x1b[44m{}{}", self.get_str(), self.do_off())}
39    fn bgmagenta(&self) -> String   {format!("\x1b[45m{}{}", self.get_str(), self.do_off())}
40    fn bgcyan(&self) -> String      {format!("\x1b[46m{}{}", self.get_str(), self.do_off())}
41    fn bgwhite(&self) -> String     {format!("\x1b[47m{}{}", self.get_str(), self.do_off())}
42
43    fn rgb(&self, rgb: &str) -> String    {format!("\x1b[38;2;{}m{}{}", rgb, self.get_str(), self.do_off())}
44
45    fn bold(&self) -> String        {format!("\x1b[1m{}{}", self.get_str(), self.do_off())}
46    fn italics(&self) -> String     {format!("\x1b[3m{}{}", self.get_str(), self.do_off())}
47    fn underline(&self) -> String   {format!("\x1b[4m{}{}", self.get_str(), self.do_off())}
48    fn strike(&self) -> String      {format!("\x1b[9m{}{}", self.get_str(), self.do_off())}
49}
50
51impl StyledString for String {
52    fn get_str(&self) -> String {
53        self.clone()
54    }
55}
56
57impl StyledString for &str {
58    fn get_str(&self) -> String {
59        self.to_string()
60    }
61}
62
63pub fn strip_styles(input: String) -> String {
64    let mut output = String::new();
65    let mut skip = false;
66    for ch in input.chars() {
67        if ch == '\x1b' {
68            skip = true;
69            continue;
70        } else if ch == 'm' && skip {
71            skip = false;
72            continue;
73        } else if skip {
74            continue;
75        } else {
76            output.push(ch);
77        }
78    }
79    output
80}
81
82pub fn run(args: ArgParser) -> Result<(), Box<dyn Error>> {
83    let mut modifier = String::new();
84
85    if args.get_bool("bold")          {modifier = modifier.bold()}
86    if args.get_bool("italics")       {modifier = modifier.italics()}
87    if args.get_bool("underline")     {modifier = modifier.underline()}
88    if args.get_bool("strike")      {modifier = modifier.strike()}
89
90    if args.get_str("fg") != "" {
91        modifier = match args.get_str("fg").as_str() {
92            "black"     => modifier.fgblack(),
93            "red"       => modifier.fgred(),
94            "green"     => modifier.fggreen(),
95            "yellow"    => modifier.fgyellow(),
96            "blue"      => modifier.fgblue(),
97            "magenta"   => modifier.fgmagenta(),
98            "cyan"      => modifier.fgcyan(),
99            "white"     => modifier.fgwhite(),
100            _ => "".to_string(),
101        }
102    }
103
104    if args.get_str("bg") != "" {
105        modifier = match args.get_str("bg").as_str() {
106            "black"     => modifier.bgblack(),
107            "red"       => modifier.bgred(),
108            "green"     => modifier.bggreen(),
109            "yellow"    => modifier.bgyellow(),
110            "blue"      => modifier.bgblue(),
111            "magenta"   => modifier.bgmagenta(),
112            "cyan"      => modifier.bgcyan(),
113            "white"     => modifier.bgwhite(),
114            _ => "".to_string(),
115        }
116    }
117
118    if args.get_str("rgb") != "" {modifier = modifier.rgb(&args.get_str("rgb"))}
119
120    modifier = modifier.remove_off();
121    if args.extra.len() > 0 {
122        let string = args.extra.join(" ");
123        println!("{}{}", modifier, string);
124    } else {
125        let stdin = io::stdin();
126        // Need to do this each line so that things like ping work
127        for line in stdin.lock().lines() {
128            let mut string = line? + "\n";
129            if !args.get_bool("dontforce") {
130                string = strip_styles(string);
131            }
132            print!("{}{}", modifier, string);
133        }
134    }
135    Ok(())
136}