use darling::{
Error, FromVariant,
ast::{Fields, Style},
};
use syn::{Ident, Type};
use crate::{
TypeExt,
derive::{Field, NamedField, Skippable},
};
pub enum VariantShape {
Unit,
Newtype(Type),
Struct(Vec<NamedField>),
}
#[derive(FromVariant)]
#[darling(attributes(config), and_then = Self::autocorrect)]
pub struct Variant {
ident: Ident,
fields: Fields<Field>,
#[darling(default)]
pub(crate) skip: bool,
}
impl Skippable for Variant {
fn is_skipped(&self) -> bool {
self.skip
}
}
impl Variant {
pub fn ident(&self) -> &Ident {
&self.ident
}
pub fn shape(&self) -> VariantShape {
if self.is_unit() {
VariantShape::Unit
} else if let Some(ty) = self.as_newtype() {
VariantShape::Newtype(ty)
} else if let Some(fields) = self.as_struct() {
VariantShape::Struct(fields)
} else {
unreachable!("variant must be either empty, newtype, or struct")
}
}
pub fn is_unit(&self) -> bool {
self.fields.is_unit()
}
pub fn as_newtype(&self) -> Option<Type> {
self.fields
.is_newtype()
.then(|| self.fields.fields.first().unwrap().ty().clone())
}
pub fn as_struct(&self) -> Option<Vec<NamedField>> {
self.fields.is_struct().then(|| {
self.fields
.fields
.iter()
.cloned()
.map(|field| field.try_into().unwrap())
.collect()
})
}
fn autocorrect(mut self) -> Result<Self, Error> {
let mut error = Error::accumulator();
if let Some(ty) = self.as_newtype()
&& ty.is_unit()
{
self.fields = Fields::new(Style::Unit, Vec::default());
}
if self.fields.is_tuple() && self.fields.fields.len() != 1 {
error.push(Error::custom("tuple enums are not supported").with_span(self.ident()));
}
if self.fields.is_struct() {
for field in self.fields.fields.iter() {
if field.ident().is_none() {
error.push(
Error::custom("struct enums requires named fields").with_span(field.ty()),
);
}
}
}
error.finish().map(|_| self)
}
}