use super::tables::*;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::borrow::Cow;
pub(crate) fn parse_class_string(class_str: &str) -> impl Iterator<Item = TokenStream> + '_ {
class_str.split_ascii_whitespace().map(parse_single_class)
}
pub(crate) fn parse_single_class(class: &str) -> TokenStream {
let method_name: Cow<str> = if class.contains('-') {
Cow::Owned(class.replace('-', "_"))
} else {
Cow::Borrowed(class)
};
if let Some(underscore_pos) = method_name.rfind('_') {
let suffix = &method_name[underscore_pos + 1..];
if let Ok(num) = suffix.parse::<f32>() {
let prefix = &method_name[..=underscore_pos];
if let Some(method) = lookup_spacing_method(prefix) {
let method_ident = syn::Ident::new(method, Span::call_site());
return quote! { .#method_ident(px(#num)) };
}
}
}
if method_name == "border" {
return quote! { .border_1() };
}
if let Some(rest) = method_name.strip_prefix("border_") {
let is_directional = matches!(rest, "t" | "b" | "l" | "r")
|| rest.starts_with("t_")
|| rest.starts_with("b_")
|| rest.starts_with("l_")
|| rest.starts_with("r_")
|| rest.starts_with("x_")
|| rest.starts_with("y_");
if !is_directional {
if rest.as_bytes().first().is_some_and(|b| b.is_ascii_digit()) {
let ident = syn::Ident::new(&method_name, Span::call_site());
return quote! { .#ident() };
} else if let Some(token) = parse_color_with_method(rest, "border_color") {
return token;
}
}
}
if let Some(rest) = method_name.strip_prefix("text_") {
if let Some(token) = parse_color_with_method(rest, "text_color") {
return token;
}
if is_valid_text_size(rest) {
let size_ident = syn::Ident::new(&method_name, Span::call_site());
return quote! { .#size_ident() };
}
}
if let Some(rest) = method_name.strip_prefix("bg_") {
if let Some(token) = parse_color_with_method(rest, "bg") {
return token;
}
}
let ident = syn::Ident::new(&method_name, Span::call_site());
quote! { .#ident() }
}
fn parse_color_with_method(color: &str, method: &str) -> Option<TokenStream> {
let hex = lookup_color(color).or_else(|| parse_arbitrary_hex(color))?;
let ident = syn::Ident::new(method, Span::call_site());
Some(quote! { .#ident(rgb(#hex)) })
}
fn parse_arbitrary_hex(s: &str) -> Option<u32> {
let inner = s.strip_prefix("[#")?.strip_suffix(']')?;
match inner.len() {
6 => u32::from_str_radix(inner, 16).ok(),
3 => {
let b = inner.as_bytes();
let d = |c: u8| -> Option<u32> { (c as char).to_digit(16) };
let r = d(b[0])?;
let g = d(b[1])?;
let bl = d(b[2])?;
Some(r << 20 | r << 16 | g << 12 | g << 8 | bl << 4 | bl)
}
_ => None,
}
}