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