use regex::Regex;
use proc_macro::TokenStream;
use syn::{parse_macro_input, Expr, Token, LitStr, parse::Parse, punctuated::Punctuated};
use quote::quote;
struct PrintlnInput {
fmt: LitStr,
args: Punctuated<Expr, Token![,]>,
}
impl Parse for PrintlnInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fmt: LitStr = input.parse()?;
let mut args = Punctuated::new();
while input.peek(Token![,]) {
let _comma: Token![,] = input.parse()?;
let expr: Expr = input.parse()?;
args.push(expr);
}
Ok(PrintlnInput { fmt, args })
}
}
mod pea_parse;
mod pea_compiled;
use crate::pea_compiled::PeaCompiled;
#[proc_macro]
pub fn pealn(item: TokenStream) -> TokenStream {
let PrintlnInput { fmt , args } = parse_macro_input!(item as PrintlnInput);
let pea_code =fmt.value();
let formatted = parse_pealn_format(&pea_code);
let expanded = quote! {
println!(#formatted, #args);
};
expanded.into()
}
#[proc_macro]
pub fn pea(item: TokenStream) -> TokenStream {
let PrintlnInput { fmt , args } = parse_macro_input!(item as PrintlnInput);
let pea_code =fmt.value();
let formatted = parse_pealn_format(&pea_code);
let expanded = quote! {
print!(#formatted, #args);
};
expanded.into()
}
fn parse_pealn_format(input: &str) -> String {
let mut result = input.to_string();
let re = Regex::new(r"\[([^\]]*)\]\(([^)]*)\)").unwrap();
let mut parse_list:Vec<pea_parse::PeaParsed> = Vec::new();
for cap in re.captures_iter(&result) {
let full_match = cap.get(0).unwrap(); parse_list.push(
pea_parse::PeaParsed {
startIndex: full_match.start(),
endIndex: full_match.end(),
fullMatch: full_match.as_str().to_string(),
modifier: cap[1].to_string(),
value: cap[2].to_string()
}
);
}
let mut formatted_result:Vec<(&pea_parse::PeaParsed ,String)> = Vec::new();
for parsed in &parse_list {
let mut prefix = String::new();
let mut suffix = String::new();
let pea_compiled = PeaCompiled::from_modifier(&parsed.modifier ,&parsed.fullMatch);
prefix.push_str("\x1b["); if !pea_compiled.styles.is_empty() {
prefix.push_str(&pea_compiled.get_style_coded());
}
if let Some((r, g, b)) = pea_compiled.foreground {
if !pea_compiled.styles.is_empty() {
prefix.push(';'); }
prefix.push_str(&format!("38;2;{};{};{}", r, g, b));
}
if let Some((r, g, b)) = pea_compiled.background {
if !pea_compiled.styles.is_empty() || pea_compiled.foreground.is_some() {
prefix.push(';'); }
prefix.push_str(&format!("48;2;{};{};{}", r, g, b));
}
prefix.push('m'); suffix.push_str("\x1b[0m");
let formatted_string = format!("{} {} {}", prefix, parsed.value, suffix);
formatted_result.push((parsed ,formatted_string));
}
for (parsed, formatted) in formatted_result.iter().rev() {
let start = parsed.startIndex;
let end = parsed.endIndex;
result.replace_range(start..end, &formatted);
}
result
}