mod from;
mod try_from;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::quote;
use syn::{DataEnum, Fields, FieldsUnnamed, Type, TypePath};
use crate::{
ContainerAttrs, FieldAttrs, FieldNamer, FieldOp, TypeRef, Types,
parse_field_attrs,
};
pub(super) fn derive_convert_enum(
ContainerAttrs {
from,
from_self,
try_from,
try_from_self,
}: &ContainerAttrs,
subject: &Ident,
data: &DataEnum,
) -> TokenStream2 {
let subject = Type::Path(TypePath {
qself: None,
path: subject.clone().into(),
});
[
from.as_ref()
.map(|attrs| from::derive_from_enum(attrs, &subject, data, false)),
from_self
.as_ref()
.map(|attrs| from::derive_from_enum(attrs, &subject, data, true)),
try_from.as_ref().map(|attrs| {
try_from::derive_try_from_enum(attrs, &subject, data, false)
}),
try_from_self.as_ref().map(|attrs| {
try_from::derive_try_from_enum(attrs, &subject, data, true)
}),
]
.into_iter()
.flatten()
.collect()
}
struct OneVariantOptions<'a, FO> {
name: &'a Ident,
context: VariantContext,
attrs: FieldAttrs<FO>,
}
struct VariantContext {
has_one_field: bool,
}
struct AllVariantsOptions<'a, FO> {
variants: Vec<OneVariantOptions<'a, FO>>,
}
impl<'a, FO: FieldOp<FieldContext = VariantContext>>
AllVariantsOptions<'a, FO>
{
fn parse(data: &'a DataEnum, filter_path: &str, types: &Types) -> Self {
let variants = &data.variants;
let variants = variants
.into_iter()
.map(|variant| {
let has_one_field = match &variant.fields {
Fields::Unnamed(FieldsUnnamed{unnamed, ..}) if unnamed.len() == 1 => {
true
},
_ if variant.fields.is_empty() => false,
_ => unimplemented!("Only C-like enums or single field unnamed variants are supported: {:?}", variant.fields),
};
let attrs = parse_field_attrs(&variant.attrs, filter_path)
.expect("Parse attributes to find field options");
attrs.check(types);
OneVariantOptions {
name: &variant.ident,
context: VariantContext { has_one_field },
attrs,
}
})
.collect();
Self { variants }
}
fn lines_n_fields(
&self,
from_self: bool,
TypeRef {
key,
from,
to,
extra_foreign_fields,
}: TypeRef,
) -> (TokenStream2, TokenStream2) {
let mut foreign_fields: Vec<_> =
extra_foreign_fields.ignores().cloned().collect();
let lines = self.variants.iter().map(|field| {
let name = field.name;
let mut namer = FieldNamer {
from_self,
name,
foreign_field: None,
from,
to,
context: &field.context,
};
let res = field.attrs.map_for(key).quote(&mut namer);
foreign_fields.extend(namer.foreign_field.into_iter().cloned());
res
});
let lines = quote!(
#(
#lines
)*
);
let foreign_fields = quote_foreign_fields(from, &foreign_fields);
(lines, foreign_fields)
}
}
fn quote_foreign_fields(from: &Type, foreign_fields: &[Ident]) -> TokenStream2 {
if foreign_fields.is_empty() {
quote!()
} else {
quote!(
{
match &value {
#(
#from::#foreign_fields{..} => {},
)*
}
}
)
}
}