moxy-derive 0.0.4

derive macros for moxy crate
Documentation
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!()
        }
    }
}