use proc_macro2::TokenStream;
use quote::quote;
use crate::core::Attrs;
use super::{DisplayOutput, Segment, SegmentKind};
pub struct ColorSyntax {
pub theme: Option<String>,
}
impl ColorSyntax {
pub fn parse(attrs: &Attrs) -> syn::Result<Self> {
let display = attrs.get("display")?;
let display_attr = display.iter().find_map(|arg| arg.as_attr());
let theme = display_attr.and_then(|attr| {
let arg = attr.args().iter().find(|a| a.path().is_ident("color"))?;
match arg.as_lit() {
Some(syn::Lit::Str(s)) => Some(s.value()),
_ => Some(String::new()),
}
});
Ok(Self { theme })
}
pub fn render(&self, output: DisplayOutput) -> DisplayOutput {
let Some(theme_name) = &self.theme else {
return output;
};
let t = super::super::themes::get(theme_name);
let nc = t.name();
let fc = t.field();
let vc = t.value();
let pc = t.punct();
let (nr, ng, nb) = (nc.r, nc.g, nc.b);
let (fr, fg, fb) = (fc.r, fc.g, fc.b);
let (vr, vg, vb) = (vc.r, vc.g, vc.b);
let (pr, pg, pb) = (pc.r, pc.g, pc.b);
let name_color = quote! { .truecolor(#nr, #ng, #nb).bold() };
let field_color = quote! { .truecolor(#fr, #fg, #fb) };
let value_color = quote! { .truecolor(#vr, #vg, #vb) };
let punct_color = quote! { .truecolor(#pr, #pg, #pb) };
let mut result = Vec::new();
for seg in output.segments {
match (&seg.kind, &seg.text, &seg.arg) {
(SegmentKind::Name, Some(text), None) => {
result.push(Segment::placeholder(
SegmentKind::Name,
quote! { #text #name_color },
));
}
(SegmentKind::FieldName, Some(text), None) => {
result.push(Segment::placeholder(
SegmentKind::FieldName,
quote! { #text #field_color },
));
}
(SegmentKind::Value, None, Some(arg)) => {
result.push(Segment::placeholder(
SegmentKind::Value,
quote! { ::std::format!("{}", #arg) #value_color },
));
}
(
SegmentKind::OpenDelim
| SegmentKind::CloseDelim
| SegmentKind::Separator
| SegmentKind::Assign,
Some(text),
None,
) if text.trim().is_empty() => {
result.push(seg);
}
(
SegmentKind::OpenDelim
| SegmentKind::CloseDelim
| SegmentKind::Separator
| SegmentKind::Assign,
Some(text),
None,
) => {
result.push(Segment::placeholder(
seg.kind,
quote! { #text #punct_color },
));
}
_ => result.push(seg),
}
}
DisplayOutput { segments: result }
}
pub fn needs_import(&self) -> bool {
self.theme.is_some()
}
pub fn import_tokens(&self) -> TokenStream {
if self.needs_import() {
quote! { use ::colored::Colorize as _; }
} else {
quote!()
}
}
}