1 | //! Implements the [`crate::untagged!()`] proc macro. |
2 | |
3 | use proc_macro::TokenStream; |
4 | use proc_macro2::TokenStream as TokenStream2; |
5 | use quote::quote; |
6 | |
7 | use crate::color_context::Context; |
8 | use crate::error::{SpanError, Error}; |
9 | use crate::format_args::{ |
10 | parse_args, get_format_string, parse_format_string, Node |
11 | }; |
12 | |
13 | /// Transforms a string literal by removing all its color tags. |
14 | pub fn get_untagged(input: TokenStream) -> Result<TokenStream2, SpanError> { |
15 | let args = parse_args(input)?; |
16 | let format_string_token = get_format_string(args.first())?; |
17 | let format_string = format_string_token.value(); |
18 | |
19 | if args.len() > 1 { |
20 | return Err(SpanError::new(Error::TooManyArgs, None)); |
21 | } |
22 | |
23 | // Split the format string into a list of nodes; each node is either a string literal (text), |
24 | // or a color code; `format!`-like placeholders will be parsed indenpendently, but as they are |
25 | // put back unchanged into the format string, it's not a problem: |
26 | let format_nodes = parse_format_string(&format_string, &format_string_token)?; |
27 | |
28 | // The final, modified format string which will be given to the `format!`-like macro: |
29 | let mut format_string = String::new(); |
30 | // Stores which colors and attributes are set while processing the format string: |
31 | let mut color_context = Context::new(); |
32 | |
33 | // Generate the final format string: |
34 | for node in format_nodes { |
35 | match node { |
36 | Node::Text(s) | Node::Placeholder(s) => { |
37 | format_string.push_str(s); |
38 | } |
39 | Node::ColorTagGroup(tag_group) => { |
40 | // Don't add the ansi codes into the final format string, but still apply to tags |
41 | // to the context in order to keep the error handling: |
42 | color_context.apply_tags(tag_group)?; |
43 | } |
44 | } |
45 | } |
46 | |
47 | Ok(quote! { #format_string }) |
48 | } |
49 | |