use proc_macro2::Ident;
use syn::{
Attribute, Data, DataEnum, DeriveInput, Expr, Field, Fields, GenericParam, Generics, ImplItem,
Token, Variant, Visibility, WhereClause,
};
use super::helper_attrs::{
HelperAttrInPlace, HelperAttrProcessor as _, VersionFilter, VersionedAttr, VersionedWhere,
};
use crate::process::Args;
use crate::util::error_sink::ErrorSink;
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub enum Input {
Type(DeriveInput),
Impl(ImplItem),
}
impl syn::parse::Parse for Input {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![impl]) {
input.parse().map(Self::Impl)
} else if lookahead.peek(Token![struct])
|| lookahead.peek(Token![union])
|| lookahead.peek(Token![enum])
{
input.parse().map(Self::Type)
} else {
Err(lookahead.error())
}
}
}
pub type GenericParamHelpers = (Option<VersionFilter>, ());
pub type ItemHelpers = (Vec<VersionedWhere>, ());
pub type FieldHelpers = (Option<VersionFilter>, ());
pub type VariantHelpers = (Option<VersionFilter>, ());
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedGenericParams {
pub highest_mentioned_version: Option<usize>,
pub params: Vec<(GenericParamHelpers, GenericParam)>,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedItemBase {
pub highest_mentioned_version: Option<usize>,
pub helpers: ItemHelpers,
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub ident: Ident,
pub generic_params: Option<ParsedGenericParams>,
pub where_clause: Option<WhereClause>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum FieldsFlavor {
Unit,
Tuple,
Struct,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedFields {
pub highest_mentioned_version: Option<usize>,
pub fields: Vec<(FieldHelpers, Field)>,
pub flavor_bias: FieldsFlavor,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedVariant {
pub highest_mentioned_version: Option<usize>,
pub helpers: VariantHelpers,
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub fields: ParsedFields,
pub discriminant: Option<(syn::token::Eq, Expr)>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StructOrUnion {
Struct,
Union,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedStructOrUnion {
pub kind: StructOrUnion,
pub base: ParsedItemBase,
pub fields: ParsedFields,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub struct ParsedEnum {
pub base: ParsedItemBase,
pub variants: Vec<ParsedVariant>,
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub enum ParsedInput {
StructOrUnion(ParsedStructOrUnion),
Enum(ParsedEnum),
}
impl ParsedGenericParams {
pub fn parse_from(
generic_params: impl IntoIterator<Item = GenericParam>,
args: &Args,
errs: &mut ErrorSink,
) -> Self {
let mut highest_mentioned_version = None;
let mut mention_version =
|v: usize| highest_mentioned_version = highest_mentioned_version.max(Some(v));
Self {
params: generic_params
.into_iter()
.map(|mut param| {
let mut helpers = GenericParamHelpers::default();
let attrs = match &mut param {
GenericParam::Lifetime(param) => &mut param.attrs,
GenericParam::Type(param) => &mut param.attrs,
GenericParam::Const(param) => &mut param.attrs,
};
*attrs = helpers.process_attrs(std::mem::take(attrs), args, errs);
if let Some(v) = helpers.highest_mentioned_version() {
mention_version(v);
}
for attr in &*attrs {
if let Some(v) = VersionedAttr::highest_mentioned_version(attr) {
mention_version(v);
}
}
(helpers, param)
})
.collect(),
highest_mentioned_version,
}
}
}
impl ParsedItemBase {
pub fn parse_from(
attrs: Vec<Attribute>,
vis: Visibility,
ident: Ident,
generics: Generics,
args: &Args,
errs: &mut ErrorSink,
) -> Self {
let generic_params = generics
.lt_token
.is_some()
.then(|| ParsedGenericParams::parse_from(generics.params, args, errs));
let mut helpers = ItemHelpers::default();
let attrs = helpers.process_attrs(attrs, args, errs);
let mut highest_mentioned_version = None;
let mut mention_version =
|v: usize| highest_mentioned_version = highest_mentioned_version.max(Some(v));
if let Some(v) = helpers.highest_mentioned_version() {
mention_version(v);
}
for attr in &attrs {
if let Some(v) = VersionedAttr::highest_mentioned_version(attr) {
mention_version(v);
}
}
if let Some(v) = generic_params
.as_ref()
.and_then(|gp| gp.highest_mentioned_version)
{
mention_version(v);
}
Self {
highest_mentioned_version,
helpers,
attrs,
vis,
ident,
generic_params,
where_clause: generics.where_clause,
}
}
}
impl ParsedFields {
pub fn parse_from(fields: Fields, args: &Args, errs: &mut ErrorSink) -> Self {
let (fields, bias) = match fields {
Fields::Unit => (None, FieldsFlavor::Unit),
Fields::Named(fields) => (Some(fields.named), FieldsFlavor::Struct),
Fields::Unnamed(fields) => (Some(fields.unnamed), FieldsFlavor::Tuple),
};
let mut highest_mentioned_version = None;
let mut mention_version =
|v: usize| highest_mentioned_version = highest_mentioned_version.max(Some(v));
let fields = fields
.into_iter()
.flatten()
.map(|mut field| {
let mut helpers = FieldHelpers::default();
field.attrs = helpers.process_attrs(field.attrs, args, errs);
if let Some(v) = helpers.highest_mentioned_version() {
mention_version(v);
}
for attr in &field.attrs {
if let Some(v) = VersionedAttr::highest_mentioned_version(attr) {
mention_version(v);
}
}
(helpers, field)
})
.collect();
Self {
highest_mentioned_version,
fields,
flavor_bias: bias,
}
}
}
impl ParsedVariant {
pub fn parse_from(variant: Variant, args: &Args, errs: &mut ErrorSink) -> Self {
let mut helpers = VariantHelpers::default();
let attrs = helpers.process_attrs(variant.attrs, args, errs);
let fields = ParsedFields::parse_from(variant.fields, args, errs);
let mut highest_mentioned_version = None;
let mut mention_version =
|v: usize| highest_mentioned_version = highest_mentioned_version.max(Some(v));
if let Some(v) = helpers.highest_mentioned_version() {
mention_version(v);
}
if let Some(v) = fields.highest_mentioned_version {
mention_version(v);
}
for attr in &attrs {
if let Some(v) = VersionedAttr::highest_mentioned_version(attr) {
mention_version(v);
}
}
Self {
highest_mentioned_version,
helpers,
attrs,
ident: variant.ident,
fields,
discriminant: variant.discriminant,
}
}
}
impl ParsedStructOrUnion {
#[inline(always)]
#[allow(clippy::too_many_arguments, reason = "the function is inlined")]
pub fn parse_from(
kind: StructOrUnion,
attrs: Vec<Attribute>,
vis: Visibility,
ident: Ident,
generics: Generics,
fields: Fields,
args: &Args,
errs: &mut ErrorSink,
) -> Self {
let base = ParsedItemBase::parse_from(attrs, vis, ident, generics, args, errs);
let fields = ParsedFields::parse_from(fields, args, errs);
Self { kind, base, fields }
}
}
impl ParsedEnum {
#[inline(always)]
pub fn parse_from(
attrs: Vec<Attribute>,
vis: Visibility,
ident: Ident,
generics: Generics,
data: DataEnum,
args: &Args,
errs: &mut ErrorSink,
) -> Self {
let base = ParsedItemBase::parse_from(attrs, vis, ident, generics, args, errs);
let variants = data
.variants
.into_iter()
.map(|variant| ParsedVariant::parse_from(variant, args, errs))
.collect();
Self { base, variants }
}
}
impl ParsedInput {
pub fn parse_from(input: DeriveInput, args: &Args) -> syn::Result<Self> {
let DeriveInput {
attrs,
vis,
ident,
generics,
data,
} = input;
let mut errs = ErrorSink::new();
match data {
Data::Struct(data) => {
let res = ParsedStructOrUnion::parse_from(
StructOrUnion::Struct,
attrs,
vis,
ident,
generics,
data.fields,
args,
&mut errs,
);
errs.finish_with(|| Self::StructOrUnion(res))
}
Data::Enum(data) => {
let res =
ParsedEnum::parse_from(attrs, vis, ident, generics, data, args, &mut errs);
errs.finish_with(|| Self::Enum(res))
}
Data::Union(data) => {
let res = ParsedStructOrUnion::parse_from(
StructOrUnion::Union,
attrs,
vis,
ident,
generics,
Fields::Named(data.fields),
args,
&mut errs,
);
errs.finish_with(|| Self::StructOrUnion(res))
}
}
}
}