identifier_derive 0.1.3

Derive macros for identifier.
Documentation
extern crate proc_macro;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, DeriveInput};

use crate::internals::{attr::Attrs, Ctxt};

pub fn expand_derive_identifier(input: &DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
    let cx = Ctxt::new();
    let attrs = Attrs::get(&cx, input);
    cx.check()?;

    let ident = &input.ident;
    let with = attrs.with();
    let params = attrs.params();

    let expanded = quote! {
        const _: () = {
            impl identifier::Identifier for #ident {
                type Id = #ident;
                type ParseError = identifier::ParseError;

                fn generate() -> Self::Id {
                    #ident(#with::generate(#params))
                }

                fn format(&self) -> String {
                    format!("{:032x}", self.0)
                }

                fn parse_str(s: &str) -> Result<Self::Id, Self::ParseError> {
                    let s = s.replace("-", "");
                    if s.len() != 32 {
                        Err(identifier::ParseError::InvalidLength)
                    } else {
                        let value = u128::from_str_radix(&s, 16).or(Err(identifier::ParseError::InvalidChars))?;
                        if (#with::validate(value, #params)) {
                            Ok(#ident(value))
                        } else {
                            Err(identifier::ParseError::Invalid)
                        }
                    }
                }
            }
        };
    };

    Ok(expanded)
}

pub fn expand_derive_display(input: &DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
    let ident = &input.ident;

    let expanded = quote! {
        impl std::fmt::Display for #ident {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{}", self.format())
            }
        }
    };

    Ok(expanded)
}

pub fn expand_derive_from_str(input: &DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
    let ident = &input.ident;
    let trait_path: syn::Path = parse_quote!(core::str::FromStr);

    let expanded = quote! {
        impl #trait_path for #ident {
            type Err = identifier::ParseError;

            fn from_str(s: &str) -> Result<Self, Self::Err> {
                #ident::parse_str(s)
            }
        }
    };

    Ok(expanded)
}