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