use darling::{FromMeta, FromVariant};
use syn::{DataEnum, Path, spanned::Spanned};
use super::{
conversion_field::{ConvertibleField, extract_convertible_fields},
conversion_meta::ConversionMethod,
};
#[derive(FromMeta)]
struct VariantConvAttrs {
#[darling(default)]
rename: Option<String>,
#[darling(default)]
skip: bool,
}
#[derive(FromVariant)]
#[darling(attributes(convert))]
struct ConvertVariant {
ident: syn::Ident,
#[darling(default)]
rename: Option<String>,
#[darling(default)]
skip: bool,
#[darling(default)]
from: Option<VariantConvAttrs>,
#[darling(default)]
try_from: Option<VariantConvAttrs>,
#[darling(default)]
into: Option<VariantConvAttrs>,
#[darling(default)]
try_into: Option<VariantConvAttrs>,
}
#[derive(Clone)]
pub(crate) struct ConversionVariant {
pub(crate) source_name: syn::Ident,
pub(crate) target_name: syn::Ident,
pub(crate) named_variant: bool,
pub(crate) fields: Vec<ConvertibleField>,
}
pub(crate) fn extract_enum_variants(
data_enum: &DataEnum,
conversion_type: ConversionMethod,
other_type: &Path,
) -> syn::Result<Vec<ConversionVariant>> {
let is_from = conversion_type.is_from();
data_enum
.variants
.iter()
.map(|variant| {
let convert_variant = match ConvertVariant::from_variant(variant) {
Ok(cv) => cv,
Err(e) => {
return Err(syn::Error::new(
variant.span(),
format!("Failed to parse variant attributes: {}", e),
));
}
};
let named_variant = matches!(variant.fields, syn::Fields::Named(_));
let variant_conv_attrs = match conversion_type {
ConversionMethod::From => convert_variant.from,
ConversionMethod::TryFrom => convert_variant.try_from,
ConversionMethod::Into => convert_variant.into,
ConversionMethod::TryInto => convert_variant.try_into,
};
if convert_variant.skip || variant_conv_attrs.as_ref().is_some_and(|attr| attr.skip) {
return Ok(None); }
let other_variant_name = variant_conv_attrs
.as_ref()
.and_then(|attrs| attrs.rename.as_ref())
.or(convert_variant.rename.as_ref())
.map(|rename| syn::Ident::new(rename, variant.span()))
.unwrap_or_else(|| convert_variant.ident.clone());
let (source_name, target_name) = if is_from {
(other_variant_name, convert_variant.ident.clone())
} else {
(convert_variant.ident.clone(), other_variant_name)
};
Ok(Some(ConversionVariant {
source_name,
target_name,
named_variant,
fields: extract_convertible_fields(&variant.fields, conversion_type, other_type)?,
}))
})
.filter_map(|result| result.transpose())
.collect()
}