1//! Implements the [`crate::untagged!()`] proc macro.
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7use crate::color_context::Context;
8use crate::error::{SpanError, Error};
9use 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.
14pub 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