clml-proc-macro 0.2.0

Implementation for the package clml
Documentation
//! Implements the [`crate::untagged!()`] proc macro.

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::LitStr;

use crate::color_context::Context;
use crate::error::{Error, SpanError};
use crate::format_args::{Node, get_format_string, parse_args, parse_format_string};

/// Transforms a string literal by removing all its color tags.
pub fn get_untagged(input: TokenStream) -> Result<TokenStream2, SpanError> {
    let args = parse_args(input)?;
    let format_string_token = get_format_string(args.first())?;
    let format_string = format_string_token.value();

    if args.len() > 1 {
        return Err(SpanError::new(Error::TooManyArgs, None));
    }

    // Split the format string into a list of nodes; each node is either a string literal (text), or
    // a color code; `format!`-like placeholders will be parsed independently, but as they are put
    // back unchanged into the format string, it's not a problem:
    let format_nodes = parse_format_string(&format_string, &format_string_token)?;

    // The final, modified format string which will be given to the `format!`-like macro:
    let mut format_string = String::new();
    // Stores which colors and attributes are set while processing the format string:
    let mut color_context = Context::new();

    // Generate the final format string:
    for node in format_nodes {
        match node {
            Node::Text(s) | Node::Placeholder(s) => {
                format_string.push_str(s);
            },
            Node::ColorTagGroup(tag_group) => {
                // Don't add the ansi codes into the final format string, but still apply to tags to
                // the context in order to keep the error handling:
                color_context.apply_tags(tag_group)?;
            },
        }
    }

    // Stamp the rebuilt literal with the span of the literal the caller wrote, so that name
    // resolution (e.g. implicit captures when the result is fed to a `format!`-like macro) happens
    // at the true call site rather than at `quote!`'s `Span::call_site()`:
    let format_string = LitStr::new(&format_string, format_string_token.span());
    Ok(quote! { #format_string })
}