1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
mod isomorphism;
use isomorphism::*;
use proc_macro2::TokenStream as TokenStream;
use quote::quote;
use syn::{self, DeriveInput, Data, Fields, Ident, Expr, spanned::Spanned, Result, Error};
/// Derive macro "Isomorphism"
///
/// * Implement traits:
/// * Isomorphism(methods of `title(&self)` and `list()`),
/// * Into<T> for &Self and Self
/// * From<T> for Self
///
/// * Only for Enum type.
///
/// # Fallbacks
/// * When Into type's value is not given at variant level attribute, default value will be used.
/// * If title is not given ant varaint level, **the variant's name (Ident)** will be used as titile.
/// * If list is not given at the top level attribute, list of each variant's default format will be returned.
///
///
/// # Ex.
/// ```
/// #[derive(Default, Isomorphism)]
/// #[isomorphism(u8, list=[A, B])]
/// pub enum ABC {
/// #[default] #[title("a")] A,
/// #[into(10)] #[title("b")] B,
/// #[into(100)] C
/// }
///
/// // Into
/// assert_eq!(Into::<u8>::into(ABC::A), 0);
/// assert_eq!(Into::<u8>::into(&ABC::B), 10);
/// // From
/// assert_eq!(Into::<ABC>::into(0u8), ABC::A);
/// assert_eq!(Into::<ABC>::into(100u8), ABC::C);
/// // List
/// assert_eq!(ABC::list(), vec![ABC::A, ABC::B]);
/// // Title
/// assert_eq!(ABC::A.title(), "A");
/// assert_eq!(ABC::C.title(), "C");
///
///
/// #[derive(Default, Isomorphism)]
/// #[isomorphism(into=[u8, i8])]
/// pub enum CD {
/// #[default] #[into([0, 1])] C,
/// #[into([0, -1])] D,
/// }
///
/// // Into
/// assert_eq!(Into::<u8>::into(CD::C), 0);
/// assert_eq!(Into::<i8>::into(CD::C), 1);
/// assert_eq!(Into::<u8>::into(CD::D), 0);
/// assert_eq!(Into::<i8>::into(CD::D), -1);
/// // From
/// assert_eq!(Into::<CD>::into(1i8), CD::C);
/// assert_eq!(Into::<CD>::into(-1i8), CD::D);
/// ```
///
#[proc_macro_derive(Isomorphism, attributes(isomorphism, into, title))]
pub fn isomorphism_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse(input).unwrap();
impl_isomorphism_macro(&ast)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
fn fields_default_format(fields: &syn::Fields) -> Result<TokenStream> {
let quoted = match fields {
Fields::Named(fields) => {
let mut quoted = TokenStream::new();
let len = fields.named.len();
for (i, field) in fields.named.iter().enumerate() {
let name = field.ident.as_ref().unwrap();
let x = if i+1==len { quote! { #name: Default::default() } } else { quote! { #name: Default::default(), } };
quoted.extend(x);
}
quote! {
{#quoted}
}
},
Fields::Unnamed(fields) => {
let mut quoted = TokenStream::new();
let len = fields.unnamed.len();
for (i, _field) in fields.unnamed.iter().enumerate() {
let x = if i+1==len { quote! { Default::default() } } else { quote! { Default::default(), } };
quoted.extend(x);
}
quote! {
(#quoted)
}
},
Fields::Unit => TokenStream::new(),
};
Ok(quoted.into())
}
fn variant_matching_format(ty_name: &Ident, variant: &syn::Variant)-> Result<TokenStream> {
let variant_name = &variant.ident;
let gen = match &variant.fields {
Fields::Named(_) => quote! { #ty_name::#variant_name {..} },
Fields::Unnamed(_) => quote! { #ty_name::#variant_name(..) },
Fields::Unit => quote! { #ty_name::#variant_name }
};
Ok(gen.into())
}
fn variant_default_format(ty_name: &Ident, variant: &syn::Variant) -> Result<TokenStream> {
let variant_name = &variant.ident;
let quoted_fields = fields_default_format(&variant.fields)?;
let gen = quote! {
#ty_name::#variant_name #quoted_fields
};
Ok(gen.into())
}