| 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 | |