| 1 | //! This module is only used when the feature `terminfo` is not activated. |
| 2 | |
| 3 | use proc_macro::TokenStream; |
| 4 | use proc_macro2::TokenStream as TokenStream2; |
| 5 | use quote::{quote, ToTokens}; |
| 6 | |
| 7 | use crate::color_context::Context; |
| 8 | use crate::error::{SpanError, Error}; |
| 9 | use crate::format_args::{ |
| 10 | parse_args, get_format_string, get_args_and_format_string, parse_format_string, Node |
| 11 | }; |
| 12 | |
| 13 | /// Common code shared between the public macros, ANSI implementation. |
| 14 | pub fn get_format_args(input: TokenStream) -> Result<TokenStream2, SpanError> { |
| 15 | let (format_string_token: LitStr, args: Punctuated) = get_args_and_format_string(input)?; |
| 16 | let format_string: String = format_string_token.value(); |
| 17 | |
| 18 | // Split the format string into a list of nodes; each node is either a string literal (text), a |
| 19 | // placeholder for a `format!`-like macro, or a color code: |
| 20 | let format_nodes: Vec> = parse_format_string(&format_string, &format_string_token)?; |
| 21 | |
| 22 | let final_format_string: TokenStream = get_format_string_from_nodes(format_nodes)?; |
| 23 | |
| 24 | // Group all the final arguments into a single iterator: |
| 25 | let args: impl Iterator = argsimpl Iterator .iter() |
| 26 | .map(|arg: &FormatArg| arg.to_token_stream()) |
| 27 | .skip(1); // Skip the original format string |
| 28 | let final_args: impl Iterator = std::iter::once(final_format_string).chain(args); |
| 29 | |
| 30 | Ok(quote! { #(#final_args),* }) |
| 31 | } |
| 32 | |
| 33 | /// Transforms a string literal by parsing its color tags. |
| 34 | pub fn get_cstr(input: TokenStream) -> Result<TokenStream2, SpanError> { |
| 35 | let args: Punctuated = parse_args(input)?; |
| 36 | let format_string_token: LitStr = get_format_string(arg:args.first())?; |
| 37 | let format_string: String = format_string_token.value(); |
| 38 | |
| 39 | if args.len() > 1 { |
| 40 | return Err(SpanError::new(err:Error::TooManyArgs, span:None)); |
| 41 | } |
| 42 | |
| 43 | // Split the format string into a list of nodes; each node is either a string literal (text), |
| 44 | // or a color code; `format!`-like placeholders will be parsed indenpendently, but as they are |
| 45 | // put back unchanged into the format string, it's not a problem: |
| 46 | let format_nodes: Vec> = parse_format_string(&format_string, &format_string_token)?; |
| 47 | get_format_string_from_nodes(format_nodes) |
| 48 | } |
| 49 | |
| 50 | /// Generates a new format string with the color tags replaced by the right ANSI codes. |
| 51 | fn get_format_string_from_nodes(nodes: Vec<Node>) -> Result<TokenStream2, SpanError> { |
| 52 | // The final, modified format string which will be given to the `format!`-like macro: |
| 53 | let mut format_string: String = String::new(); |
| 54 | // Stores which colors and attributes are set while processing the format string: |
| 55 | let mut color_context: Context<'_> = Context::new(); |
| 56 | |
| 57 | // Generate the final format string: |
| 58 | for node: Node<'_> in nodes { |
| 59 | match node { |
| 60 | Node::Text(s: &str) | Node::Placeholder(s: &str) => { |
| 61 | format_string.push_str(string:s); |
| 62 | } |
| 63 | Node::ColorTagGroup(tag_group: Vec>) => { |
| 64 | let ansi_string: String = color_context.ansi_apply_tags(tag_group)?; |
| 65 | format_string.push_str(&ansi_string); |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | Ok(quote! { #format_string }) |
| 71 | } |
| 72 | |