color_print_proc_macro/
lib.rs

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
5extern crate proc_macro;
6
7#[macro_use]
8mod util;
9#[cfg(not(feature = "terminfo"))]
10mod ansi;
11#[cfg(not(feature = "terminfo"))]
12mod ansi_constants;
13mod color_context;
14mod error;
15mod format_args;
16mod parse;
17#[cfg(feature = "terminfo")]
18mod terminfo;
19mod untagged;
20
21use proc_macro::TokenStream;
22use quote::{quote, ToTokens};
23use 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"))]
41pub fn cformat(input: TokenStream) -> TokenStream {
42    get_macro("format", input, false)
43}
44
45/// The same as `format!()`, but parses color tags.
46#[proc_macro]
47#[cfg(feature = "terminfo")]
48pub 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"))]
62pub fn cprint(input: TokenStream) -> TokenStream {
63    get_macro("print", input, false)
64}
65
66/// The same as `print!()`, but parses color tags.
67#[proc_macro]
68#[cfg(feature = "terminfo")]
69pub 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"))]
83pub fn ceprint(input: TokenStream) -> TokenStream {
84    get_macro("eprint", input, false)
85}
86
87/// The same as `eprint!()`, but parses color tags.
88#[proc_macro]
89#[cfg(feature = "terminfo")]
90pub 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"))]
104pub fn cprintln(input: TokenStream) -> TokenStream {
105    get_macro("println", input, false)
106}
107
108/// The same as `println!()`, but parses color tags.
109#[proc_macro]
110#[cfg(feature = "terminfo")]
111pub 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"))]
125pub fn ceprintln(input: TokenStream) -> TokenStream {
126    get_macro("eprintln", input, false)
127}
128
129/// The same as `eprintln!()`, but parses color tags.
130#[proc_macro]
131#[cfg(feature = "terminfo")]
132pub 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"))]
139pub fn cwrite(input: TokenStream) -> TokenStream {
140    get_macro("write", input, true)
141}
142
143/// The same as `write!()`, but parses color tags.
144#[proc_macro]
145#[cfg(feature = "terminfo")]
146pub 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"))]
153pub fn cwriteln(input: TokenStream) -> TokenStream {
154    get_macro("writeln", input, true)
155}
156
157/// The same as `writeln!()`, but parses color tags.
158#[proc_macro]
159#[cfg(feature = "terminfo")]
160pub 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]
178pub fn cstr(input: TokenStream) -> TokenStream {
179    crate::ansi::get_cstr(input)
180        .unwrap_or_else(|err| 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]
196pub fn untagged(input: TokenStream) -> TokenStream {
197    crate::untagged::get_untagged(input)
198        .unwrap_or_else(|err| 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]
208pub fn cstr(_: TokenStream) -> TokenStream {
209    panic!("Macro cstr!() cannot be used with terminfo feature")
210}
211
212struct WriteInput {
213    dst: Expr,
214    rest: TokenStream,
215}
216
217impl 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 = input.parse_terminated(Expr::parse, Comma)?;
222        let rest = quote! { #rest }.into(); // Not sure how to do best?
223        Ok(Self { dst, rest })
224    }
225}
226
227/// Renders a whole processed macro.
228fn get_macro(macro_name: &str, input: TokenStream, is_write_macro: bool) -> TokenStream {
229    let macro_name = util::ident(macro_name);
230    let fmt_args = |input_tail| {
231        #[cfg(not(feature = "terminfo"))]
232        let format_args = 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| err.to_token_stream())
236    };
237
238    if is_write_macro {
239        let WriteInput { dst, rest } = parse_macro_input!(input);
240        let format_args = fmt_args(rest);
241        (quote! { #macro_name!(#dst, #format_args) }).into()
242    } else {
243        let format_args = fmt_args(input);
244        (quote! { #macro_name!(#format_args) }).into()
245    }
246}