moxy-derive 0.0.4

derive macros for moxy crate
Documentation
#[cfg(feature = "color")]
pub mod color;
pub mod custom;
pub mod style;

use proc_macro2::TokenStream;
use quote::quote;

use crate::core::Field;

pub struct DisplayOptions<'a> {
    pub name: String,
    pub fields: &'a [&'a Field],
    pub is_named: bool,
    pub use_self: bool,
}

pub enum SegmentKind {
    Name,
    FieldName,
    Value,
    OpenDelim,
    CloseDelim,
    Separator,
    Assign,
}

pub struct Segment {
    #[allow(dead_code)]
    pub kind: SegmentKind,
    pub text: Option<String>,
    pub arg: Option<TokenStream>,
}

impl Segment {
    pub fn literal(kind: SegmentKind, text: &str) -> Self {
        Self {
            kind,
            text: Some(text.to_string()),
            arg: None,
        }
    }

    pub fn placeholder(kind: SegmentKind, arg: TokenStream) -> Self {
        Self {
            kind,
            text: None,
            arg: Some(arg),
        }
    }
}

pub struct DisplayOutput {
    pub segments: Vec<Segment>,
}

impl DisplayOutput {
    pub fn new() -> Self {
        Self {
            segments: Vec::new(),
        }
    }

    pub fn push(&mut self, seg: Segment) {
        self.segments.push(seg);
    }
}

impl quote::ToTokens for DisplayOutput {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let mut fmt = String::new();
        let mut args: Vec<TokenStream> = Vec::new();

        for seg in &self.segments {
            match (&seg.text, &seg.arg) {
                (Some(text), None) => {
                    fmt.push_str(&text.replace('{', "{{").replace('}', "}}"));
                }
                (None, Some(arg)) => {
                    fmt.push_str("{}");
                    args.push(arg.clone());
                }
                _ => {}
            }
        }

        let output = if args.is_empty() {
            quote! { ::std::write!(f, #fmt) }
        } else {
            quote! { ::std::write!(f, #fmt, #(#args),*) }
        };

        tokens.extend(output);
    }
}