1 | //! This internal library provides the procedural macros needed by the crate [`color-print`]. |
2 | //! |
3 | //! [`color-print`]: https://crates.io/crates/color-print |
4 | |
5 | extern crate proc_macro; |
6 | |
7 | #[macro_use ] |
8 | mod util; |
9 | #[cfg (not(feature = "terminfo" ))] |
10 | mod ansi; |
11 | #[cfg (not(feature = "terminfo" ))] |
12 | mod ansi_constants; |
13 | mod color_context; |
14 | mod error; |
15 | mod format_args; |
16 | mod parse; |
17 | #[cfg (feature = "terminfo" )] |
18 | mod terminfo; |
19 | mod untagged; |
20 | |
21 | use proc_macro::TokenStream; |
22 | use quote::{quote, ToTokens}; |
23 | use syn::{ |
24 | parse::{Parse, ParseStream}, |
25 | parse_macro_input, |
26 | token::Comma, |
27 | Expr, |
28 | }; |
29 | |
30 | /// The same as `format!()`, but parses color tags. |
31 | /// |
32 | /// #### Example |
33 | /// |
34 | /// ``` |
35 | /// # use color_print_proc_macro::cformat; |
36 | /// let s: String = cformat!("A <g>green</> word, {}" , "placeholders are allowed" ); |
37 | /// assert_eq!(s, "A \u{1b}[32mgreen \u{1b}[39m word, placeholders are allowed" ); |
38 | /// ``` |
39 | #[proc_macro ] |
40 | #[cfg (not(feature = "terminfo" ))] |
41 | pub fn cformat(input: TokenStream) -> TokenStream { |
42 | get_macro(macro_name:"format" , input, is_write_macro:false) |
43 | } |
44 | |
45 | /// The same as `format!()`, but parses color tags. |
46 | #[proc_macro ] |
47 | #[cfg (feature = "terminfo" )] |
48 | pub fn cformat(input: TokenStream) -> TokenStream { |
49 | get_macro("format" , input, false) |
50 | } |
51 | |
52 | /// The same as `print!()`, but parses color tags. |
53 | /// |
54 | /// #### Example |
55 | /// |
56 | /// ``` |
57 | /// # use color_print_proc_macro::cprint; |
58 | /// cprint!("A <g>green</> word, {}" , "placeholders are allowed" ); |
59 | /// ``` |
60 | #[proc_macro ] |
61 | #[cfg (not(feature = "terminfo" ))] |
62 | pub fn cprint(input: TokenStream) -> TokenStream { |
63 | get_macro(macro_name:"print" , input, is_write_macro:false) |
64 | } |
65 | |
66 | /// The same as `print!()`, but parses color tags. |
67 | #[proc_macro ] |
68 | #[cfg (feature = "terminfo" )] |
69 | pub fn cprint(input: TokenStream) -> TokenStream { |
70 | get_macro("print" , input, false) |
71 | } |
72 | |
73 | /// The same as `eprint!()`, but parses color tags. |
74 | /// |
75 | /// #### Example |
76 | /// |
77 | /// ``` |
78 | /// # use color_print_proc_macro::ceprint; |
79 | /// ceprint!("A <g>green</> word, {}" , "placeholders are allowed" ); |
80 | /// ``` |
81 | #[proc_macro ] |
82 | #[cfg (not(feature = "terminfo" ))] |
83 | pub fn ceprint(input: TokenStream) -> TokenStream { |
84 | get_macro(macro_name:"eprint" , input, is_write_macro:false) |
85 | } |
86 | |
87 | /// The same as `eprint!()`, but parses color tags. |
88 | #[proc_macro ] |
89 | #[cfg (feature = "terminfo" )] |
90 | pub fn ceprint(input: TokenStream) -> TokenStream { |
91 | get_macro("eprint" , input, false) |
92 | } |
93 | |
94 | /// The same as `println!()`, but parses color tags. |
95 | /// |
96 | /// #### Example |
97 | /// |
98 | /// ``` |
99 | /// # use color_print_proc_macro::cprintln; |
100 | /// cprintln!("A <g>green</> word, {}" , "placeholders are allowed" ); |
101 | /// ``` |
102 | #[proc_macro ] |
103 | #[cfg (not(feature = "terminfo" ))] |
104 | pub fn cprintln(input: TokenStream) -> TokenStream { |
105 | get_macro(macro_name:"println" , input, is_write_macro:false) |
106 | } |
107 | |
108 | /// The same as `println!()`, but parses color tags. |
109 | #[proc_macro ] |
110 | #[cfg (feature = "terminfo" )] |
111 | pub fn cprintln(input: TokenStream) -> TokenStream { |
112 | get_macro("println" , input, false) |
113 | } |
114 | |
115 | /// The same as `eprintln!()`, but parses color tags. |
116 | /// |
117 | /// #### Example |
118 | /// |
119 | /// ``` |
120 | /// # use color_print_proc_macro::ceprintln; |
121 | /// ceprintln!("A <g>green</> word, {}" , "placeholders are allowed" ); |
122 | /// ``` |
123 | #[proc_macro ] |
124 | #[cfg (not(feature = "terminfo" ))] |
125 | pub fn ceprintln(input: TokenStream) -> TokenStream { |
126 | get_macro(macro_name:"eprintln" , input, is_write_macro:false) |
127 | } |
128 | |
129 | /// The same as `eprintln!()`, but parses color tags. |
130 | #[proc_macro ] |
131 | #[cfg (feature = "terminfo" )] |
132 | pub fn ceprintln(input: TokenStream) -> TokenStream { |
133 | get_macro("eprintln" , input, false) |
134 | } |
135 | |
136 | /// The same as `write!()`, but parses color tags. |
137 | #[proc_macro ] |
138 | #[cfg (not(feature = "terminfo" ))] |
139 | pub fn cwrite(input: TokenStream) -> TokenStream { |
140 | get_macro(macro_name:"write" , input, is_write_macro:true) |
141 | } |
142 | |
143 | /// The same as `write!()`, but parses color tags. |
144 | #[proc_macro ] |
145 | #[cfg (feature = "terminfo" )] |
146 | pub fn cwrite(input: TokenStream) -> TokenStream { |
147 | get_macro("write" , input, true) |
148 | } |
149 | |
150 | /// The same as `writeln!()`, but parses color tags. |
151 | #[proc_macro ] |
152 | #[cfg (not(feature = "terminfo" ))] |
153 | pub fn cwriteln(input: TokenStream) -> TokenStream { |
154 | get_macro(macro_name:"writeln" , input, is_write_macro:true) |
155 | } |
156 | |
157 | /// The same as `writeln!()`, but parses color tags. |
158 | #[proc_macro ] |
159 | #[cfg (feature = "terminfo" )] |
160 | pub fn cwriteln(input: TokenStream) -> TokenStream { |
161 | get_macro("writeln" , input, true) |
162 | } |
163 | |
164 | /// Colorizes a string literal, without formatting the `format!`-like placeholders. |
165 | /// |
166 | /// * Accepts only one argument; |
167 | /// * Will panic if feature `terminfo` is activated. |
168 | /// |
169 | /// #### Example |
170 | /// |
171 | /// ``` |
172 | /// # use color_print_proc_macro::cstr; |
173 | /// let s: &str = cstr!("A <g>green</> word" ); |
174 | /// assert_eq!(s, "A \u{1b}[32mgreen \u{1b}[39m word" ); |
175 | /// ``` |
176 | #[cfg (not(feature = "terminfo" ))] |
177 | #[proc_macro ] |
178 | pub fn cstr(input: TokenStream) -> TokenStream { |
179 | crateTokenStream::ansi::get_cstr(input) |
180 | .unwrap_or_else(|err: SpanError| err.to_token_stream()) |
181 | .into() |
182 | } |
183 | |
184 | /// Removes all the color tags from the given string literal. |
185 | /// |
186 | /// Accepts only one argument. |
187 | /// |
188 | /// #### Example |
189 | /// |
190 | /// ``` |
191 | /// # use color_print_proc_macro::untagged; |
192 | /// let s: &str = untagged!("A <g>normal</> word" ); |
193 | /// assert_eq!(s, "A normal word" ); |
194 | /// ``` |
195 | #[proc_macro ] |
196 | pub fn untagged(input: TokenStream) -> TokenStream { |
197 | crateTokenStream::untagged::get_untagged(input) |
198 | .unwrap_or_else(|err: SpanError| err.to_token_stream()) |
199 | .into() |
200 | } |
201 | |
202 | /// Colorizes a string literal, without formatting the `format!`-like placeholders. |
203 | /// |
204 | /// * Accepts only one argument; |
205 | /// * Will panic if feature `terminfo` is activated. |
206 | #[cfg (feature = "terminfo" )] |
207 | #[proc_macro ] |
208 | pub fn cstr(_: TokenStream) -> TokenStream { |
209 | panic!("Macro cstr!() cannot be used with terminfo feature" ) |
210 | } |
211 | |
212 | struct WriteInput { |
213 | dst: Expr, |
214 | rest: TokenStream, |
215 | } |
216 | |
217 | impl Parse for WriteInput { |
218 | fn parse(input: ParseStream) -> syn::parse::Result<Self> { |
219 | let dst: Expr = input.parse()?; |
220 | let _: Comma = input.parse()?; |
221 | let rest: Punctuated = input.parse_terminated(parser:Expr::parse, separator:Comma)?; |
222 | let rest: TokenStream = quote! { #rest }.into(); // Not sure how to do best? |
223 | Ok(Self { dst, rest }) |
224 | } |
225 | } |
226 | |
227 | /// Renders a whole processed macro. |
228 | fn get_macro(macro_name: &str, input: TokenStream, is_write_macro: bool) -> TokenStream { |
229 | let macro_name: Ident = util::ident(macro_name); |
230 | let fmt_args: impl Fn(TokenStream) -> TokenStream = |input_tail: TokenStream| { |
231 | #[cfg (not(feature = "terminfo" ))] |
232 | let format_args: Result = crate::ansi::get_format_args(input_tail); |
233 | #[cfg (feature = "terminfo" )] |
234 | let format_args = crate::terminfo::get_format_args(input_tail); |
235 | format_args.unwrap_or_else(|err: SpanError| err.to_token_stream()) |
236 | }; |
237 | |
238 | if is_write_macro { |
239 | let WriteInput { dst: Expr, rest: TokenStream } = parse_macro_input!(input); |
240 | let format_args: TokenStream = fmt_args(input_tail:rest); |
241 | (quote! { #macro_name!(#dst, #format_args) }).into() |
242 | } else { |
243 | let format_args: TokenStream = fmt_args(input); |
244 | (quote! { #macro_name!(#format_args) }).into() |
245 | } |
246 | } |
247 | |