pcd-rs-derive 0.13.0

Derive macros for pcd-rs crate
Documentation
use once_cell::sync::Lazy;
use regex::Regex;
use syn::{
    braced,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    token, Attribute, Error, Field, Ident, LitStr, Result, Token, Visibility,
};

pub struct ItemStruct {
    _attrs: Vec<Attribute>,
    _vis: Visibility,
    _struct_token: Token![struct],
    pub ident: Ident,
    _brace_token: token::Brace,
    pub fields: Punctuated<Field, Token![,]>,
}

impl Parse for ItemStruct {
    fn parse(input: ParseStream) -> Result<Self> {
        let content;
        Ok(ItemStruct {
            _attrs: input.call(Attribute::parse_outer)?,
            _vis: input.parse()?,
            _struct_token: input.parse()?,
            ident: input.parse()?,
            _brace_token: braced!(content in input),
            fields: content.parse_terminated(Field::parse_named, Token![,])?,
        })
    }
}

pub struct AttrList {
    pub options: Punctuated<AttrOption, Token![,]>,
}

impl Parse for AttrList {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            options: input.parse_terminated(AttrOption::parse, Token![,])?,
        })
    }
}

pub enum AttrOption {
    Rename(RenameAttr),
    Ignore(IgnoreAttr),
}

impl AttrOption {
    pub fn as_rename(&self) -> Option<&RenameAttr> {
        if let Self::Rename(v) = self {
            Some(v)
        } else {
            None
        }
    }

    pub fn as_ignore(&self) -> Option<&IgnoreAttr> {
        if let Self::Ignore(v) = self {
            Some(v)
        } else {
            None
        }
    }
}

impl From<RenameAttr> for AttrOption {
    fn from(v: RenameAttr) -> Self {
        Self::Rename(v)
    }
}

impl From<IgnoreAttr> for AttrOption {
    fn from(v: IgnoreAttr) -> Self {
        Self::Ignore(v)
    }
}

pub struct RenameAttr {
    pub ident: Ident,
    _eq_token: Token![=],
    _lit: LitStr,
    pub rename: String,
}

pub struct IgnoreAttr {
    pub ident: Ident,
}

impl Parse for AttrOption {
    fn parse(input: ParseStream) -> Result<Self> {
        let ident: Ident = input.parse()?;

        let attr: Self = match ident.to_string().as_str() {
            "rename" => {
                static NAME_REGEX: Lazy<Regex> =
                    Lazy::new(|| Regex::new(r"^[[:word:]]+$").unwrap());

                let eq_token = input.parse()?;
                let lit: LitStr = input.parse()?;
                let rename = lit.value();

                NAME_REGEX
                    .find(&rename)
                    .ok_or_else(|| Error::new(lit.span(), "invalid name"))?;

                RenameAttr {
                    ident,
                    _eq_token: eq_token,
                    _lit: lit,
                    rename,
                }
                .into()
            }
            "ignore" => IgnoreAttr { ident }.into(),
            name => {
                return Err(Error::new(
                    ident.span(),
                    format!("invalid attribute '{}'", name),
                ));
            }
        };

        Ok(attr)
    }
}