1use detect::{ArgumentType, error, parse_arg};
7use error::AnsicMacroError;
8use proc_macro::{Delimiter, Literal, TokenStream, TokenTree};
9use styles::AnsiStyle;
10
11mod detect;
12mod error;
13mod styles;
14
15const ANSI_PREFIX: &'static str = "\x1b[";
16
17struct AnsiArg<'a> {
18 pub name: &'a str,
19 pub rgb: Option<(u8, u8, u8)>,
20 pub bright: bool,
21 pub bg: bool,
22}
23
24impl<'a> AnsiArg<'a> {
25 fn generate_ansi(self) -> Option<String> {
26 if let Some(style) = AnsiStyle::to_style(self.name) {
27 return Some(style.code());
28 } else if let Some(color) = AnsiStyle::to_color(self.bg, self.bright, self.name, self.rgb) {
29 return Some(color.code());
30 }
31
32 None
33 }
34}
35
36fn generate_ansiarg(args: Vec<ArgumentType>, name: &str, rgb: Option<(u8, u8, u8)>) -> AnsiArg {
37 let mut profile = AnsiArg {
38 name,
39 bright: false,
40 bg: false,
41 rgb,
42 };
43
44 for arg in args {
45 match arg {
46 ArgumentType::Background => profile.bg = true,
47 ArgumentType::Bright => profile.bright = true,
48 }
49 }
50
51 profile
52}
53
54fn generate(args: Vec<String>) -> Result<String, syn::Error> {
55 let mut args_iter = args.iter().peekable();
56
57 let mut name: Option<&str> = None;
58 let mut rgb: Option<(u8, u8, u8)> = None;
59 let mut args: Vec<ArgumentType> = Vec::new();
60
61 while let Some(arg) = args_iter.next() {
62 if arg == "rgb" {
63 if let Some(_) = name {
64 return Err(AnsicMacroError::MultipleColorStyleArgs.into());
65 }
66
67 name = Some(arg);
68 let (mut r, mut g, mut b): (u8, u8, u8) = (0, 0, 0);
69
70 for i in 0..3 {
71 if let Some(res) = args_iter.next() {
72 if let Ok(num) = res.parse::<u8>() {
73 match i {
74 0 => r = num,
75 1 => g = num,
76 2 => b = num,
77 _ => return Err(AnsicMacroError::Unreachable.into()),
78 }
79 } else {
80 return Err(AnsicMacroError::InvalidRgbArg(i, res).into());
81 }
82 } else {
83 return Err(AnsicMacroError::MissingRgbArg(i).into());
84 }
85 }
86
87 rgb = Some((r, g, b));
88 } else if let Some(color) = AnsiStyle::to_color(false, false, arg, None) {
89 if let Some(_) = name {
90 return Err(AnsicMacroError::MultipleColorStyleArgs.into());
91 }
92
93 name = Some(arg)
94 } else if let Some(style) = AnsiStyle::to_style(arg) {
95 if let Some(_) = name {
96 return Err(AnsicMacroError::MultipleColorStyleArgs.into());
97 }
98
99 name = Some(arg)
100 } else if let Some(targ) = parse_arg(arg) {
101 args.push(targ);
102 } else {
103 return Err(AnsicMacroError::InvalidStyleAndColor(arg).into());
104 }
105 }
106
107 if let Some(fname) = name {
108 let arg = generate_ansiarg(args, fname, rgb);
109
110 if let Some(data) = arg.generate_ansi() {
111 return Ok(data);
112 }
113
114 return Err(AnsicMacroError::Unreachable.into());
115 }
116
117 Err(AnsicMacroError::NoStyleOrColorTarget.into())
118}
119
120fn generate_ansi(args: Vec<Vec<String>>) -> Result<String, syn::Error> {
121 let mut ansi_code = ANSI_PREFIX.to_string();
122
123 let argslen = args.len();
124
125 for (i, arg) in args.into_iter().enumerate() {
126 let parsed = generate(arg)?;
127
128 if argslen - 1 == i {
129 ansi_code.push_str(&format!("{}m", parsed));
130 } else {
131 ansi_code.push_str(&format!("{};", parsed));
132 }
133 }
134
135 Ok(ansi_code)
136}
137
138fn generate_tokens(tokens: TokenStream) -> Result<Vec<Vec<String>>, syn::Error> {
139 let mut result = Vec::new();
140 let mut current = Vec::new();
141
142 let mut token_iter = tokens.into_iter().peekable();
143
144 while let Some(token) = token_iter.next() {
145 match &token {
146 TokenTree::Ident(ident) => {
147 current.push(ident.to_string());
148
149 if ident.to_string() == "rgb" {
150 match token_iter.peek() {
151 Some(TokenTree::Group(parens)) => {
152 if parens.delimiter() == Delimiter::Parenthesis {
153 let pstream = parens.stream();
155 let mut stream_iter = pstream.into_iter().peekable();
156
157 while let Some(token) = stream_iter.next() {
158 match token {
159 TokenTree::Literal(lit) => {
160 let s = lit.to_string();
161
162 if let Ok(num) = s.parse::<u8>() {
163 current.push(s);
164
165 match stream_iter.peek() {
166 Some(TokenTree::Punct(p))
167 if p.as_char() == ',' =>
168 {
169 stream_iter.next();
170 }
171 _ => {
172 result.push(current);
173 current = Vec::new();
174 }
175 }
176 } else {
177 return Err(AnsicMacroError::RgbArgNotU8(&s).into());
178 }
179 }
180 _ => {}
181 }
182 }
183 }
184 }
185 _ => {
186 return Err(AnsicMacroError::ExpectedRgbSyntax.into());
187 }
188 }
189
190 continue;
191 }
192
193 match token_iter.peek() {
194 Some(TokenTree::Punct(p)) if p.as_char() == '.' => {
195 token_iter.next();
196 }
197 _ => {
198 result.push(current);
199 current = Vec::new();
200 }
201 }
202 }
203 _ => {}
204 }
205 }
206
207 Ok(result)
208}
209
210#[proc_macro]
211pub fn ansi(input: TokenStream) -> TokenStream {
212 let tokens = generate_tokens(input);
213
214 if let Ok(args) = tokens {
215 let result = generate_ansi(args);
216
217 if let Ok(ansi_code) = result {
218 let lit = TokenTree::Literal(Literal::string(&ansi_code));
219 return TokenStream::from(lit);
220 } else if let Err(err) = result {
221 return err.to_compile_error().into();
222 } else {
223 let err: syn::Error = AnsicMacroError::Unreachable.into();
224
225 return err.to_compile_error().into();
226 }
227 } else if let Err(err) = tokens {
228 return err.to_compile_error().into();
229 } else {
230 let err: syn::Error = AnsicMacroError::Unreachable.into();
231
232 return err.to_compile_error().into();
233 }
234}