1//! This module is only used when the feature `terminfo` is not activated.
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::{quote, ToTokens};
6
7use crate::color_context::Context;
8use crate::error::{SpanError, Error};
9use 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.
14pub 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.
34pub 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.
51fn 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