use proc_macro::TokenStream;
use quote::quote;
use syn::{LitStr, parse_macro_input};
fn load_registry() {
let out_dir = std::env::var("OUT_DIR").unwrap_or_default();
let path = std::path::Path::new(&out_dir).join("farben_registry.lsv");
if let Ok(content) = std::fs::read_to_string(&path) {
let mut sections = content.splitn(2, "---\n");
let styles_section = sections.next().unwrap_or("");
let prefixes_section = sections.next().unwrap_or("");
for line in styles_section.lines() {
if line.is_empty() {
continue;
}
let (key, value) = line.split_once('=').unwrap();
farben_core::registry::insert_style(
key,
farben_core::ansi::Style::parse(format!("[{value}]"))
.unwrap_or_else(|e| panic!("farben: invalid style in registry '{key}': {e}")),
);
}
for line in prefixes_section.lines() {
if line.is_empty() {
continue;
}
let (key, value) = line.split_once('=').unwrap();
farben_core::registry::set_prefix(key, value)
.unwrap_or_else(|e| panic!("farben: failure while setting prefix '{key}': {e}"));
}
}
}
#[proc_macro]
pub fn color(input: TokenStream) -> TokenStream {
load_registry();
let input = parse_macro_input!(input as LitStr);
let value = input.value();
let tokens = match farben_core::lexer::tokenize(&value) {
Ok(t) => t,
Err(e) => {
let msg = e.to_string();
return syn::Error::new_spanned(&input, msg)
.to_compile_error()
.into();
}
};
let styled = format!("{}\x1b[0m", farben_core::parser::render_forced(tokens));
let plain = farben_core::strip::strip_markup(&input.value());
quote! {
::farben::FarbenStr { styled: #styled, plain: #plain }
}
.into()
}
#[proc_macro]
pub fn colorb(input: TokenStream) -> TokenStream {
load_registry();
let input = parse_macro_input!(input as LitStr);
let value = input.value();
let tokens = match farben_core::lexer::tokenize(&value) {
Ok(t) => t,
Err(e) => {
let msg = e.to_string();
return syn::Error::new_spanned(&input, msg)
.to_compile_error()
.into();
}
};
let styled = farben_core::parser::render_forced(tokens);
let plain = farben_core::strip::strip_markup(&input.value());
quote! {
::farben::FarbenStr { styled: #styled, plain: #plain }
}
.into()
}
#[proc_macro]
pub fn validate_color(input: TokenStream) -> TokenStream {
load_registry();
let input = parse_macro_input!(input as LitStr);
let value = input.value();
match farben_core::lexer::tokenize(&value) {
Ok(_) => quote! { #input }.into(),
Err(e) => {
let msg = e.to_string();
syn::Error::new_spanned(&input, msg)
.to_compile_error()
.into()
}
}
}
#[cfg(feature = "markdown")]
#[proc_macro]
pub fn markdown(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let result = farben_md::renderer::render(&farben_md::lexer::tokenize(&input.value()));
quote! { #result }.into()
}