spire_enum_macros 0.1.0

Procedural macros to facilitate enum usage, such as when delegating implementations and/or generating variant types.
Documentation
use super::*;

mod attr_kw {
	use super::*;
	custom_keyword!(delegator);
}

#[derive(Parse, ToTokens)]
pub enum VariantFields<Attr> {
	Named(Brace<Punctuated<VariantFieldNamed<Attr>, Token![,]>>),
	Unnamed(Paren<Punctuated<VariantFieldUnnamed<Attr>, Token![,]>>),
	Unit,
}

impl<T> VariantFields<T> {
	pub fn field_count(&self) -> usize {
		match self {
			VariantFields::Named(fields) => fields.len(),
			VariantFields::Unnamed(fields) => fields.len(),
			VariantFields::Unit => 0,
		}
	}
}

#[derive(Parse, ToTokens)]
pub struct VariantFieldNamed<Attr> {
	pub attrs: Any<InputAttribute<Attr>>,
	pub ident: Ident,
	pub colon_token: Option<Token![:]>,
	pub ty: Type,
}

#[derive(Parse, ToTokens)]
pub struct VariantFieldUnnamed<Attr> {
	pub attrs: Any<InputAttribute<Attr>>,
	pub ty: Type,
}

#[allow(clippy::large_enum_variant)]
#[derive(Parse, ToTokens)]
pub enum VariantFieldMeta {
	Delegator(attr_kw::delegator),
	Syn(Meta),
}

#[derive(ToTokens)]
pub enum SaneVariantFields {
	Named(SaneVariantFieldsNamed),
	Unnamed(SaneVariantFieldsUnnamed),
	Unit,
}

impl SaneVariantFields {
	pub fn delegator_field_kw(&self) -> Option<&attr_kw::delegator> {
		match self {
			SaneVariantFields::Named(named) => named.delegator.as_ref().map(|(kw, _)| kw),
			SaneVariantFields::Unnamed(unnamed) => unnamed.delegator.as_ref().map(|(kw, _)| kw),
			SaneVariantFields::Unit => None,
		}
	}
}

pub struct SaneVariantFieldsNamed {
	pub fields: Brace<Punctuated<VariantFieldNamed<Meta>, Token![,]>>,
	pub delegator: Option<(attr_kw::delegator, Ident)>,
}

pub struct SaneVariantFieldsUnnamed {
	pub fields: Paren<Punctuated<VariantFieldUnnamed<Meta>, Token![,]>>,
	pub delegator: Option<(attr_kw::delegator, usize)>,
}

pub fn sanitize_variant_fields(
	input: VariantFields<VariantFieldMeta>,
) -> Result<SaneVariantFields> {
	match input {
		VariantFields::Named(fields) => {
			let (brace_token, fields) = fields.into_parts();

			let mut delegator: Option<(attr_kw::delegator, Ident)> = None;
			let mut new_fields = Punctuated::<VariantFieldNamed<Meta>, Token![,]>::new();

			for pair in fields.into_pairs() {
				let (input_field, comma) = pair.into_tuple();
				let (new_field, delegator_option) = sanitize_field_named(input_field)?;
				if let Some(second) = delegator_option {
					if let Some((first, _)) = delegator {
						err_expected_only_one!(first, second);
					} else {
						delegator = Some((second, new_field.ident.clone()));
					}
				}

				new_fields.push_value(new_field);
				if let Some(comma) = comma {
					new_fields.push_punct(comma);
				}
			}

			Ok(SaneVariantFields::Named(SaneVariantFieldsNamed {
				fields: Brace::from((brace_token, new_fields)),
				delegator,
			}))
		}
		VariantFields::Unnamed(fields) => {
			let (paren_token, fields) = fields.into_parts();

			let mut delegator: Option<(attr_kw::delegator, usize)> = None;
			let mut new_fields = Punctuated::<VariantFieldUnnamed<Meta>, Token![,]>::new();

			for (idx, pair) in fields.into_pairs().enumerate() {
				let (input_field, comma) = pair.into_tuple();
				let (new_field, delegator_option) = sanitize_field_unnamed(input_field)?;
				if let Some(second) = delegator_option {
					if let Some((first, _)) = delegator {
						err_expected_only_one!(first, second);
					} else {
						delegator = Some((second, idx));
					}
				}

				new_fields.push_value(new_field);
				if let Some(comma) = comma {
					new_fields.push_punct(comma);
				}
			}

			Ok(SaneVariantFields::Unnamed(SaneVariantFieldsUnnamed {
				fields: Paren::from((paren_token, new_fields)),
				delegator,
			}))
		}
		VariantFields::Unit => Ok(SaneVariantFields::Unit),
	}
}

fn sanitize_field_named(
	field: VariantFieldNamed<VariantFieldMeta>,
) -> Result<(VariantFieldNamed<Meta>, Option<attr_kw::delegator>)> {
	let VariantFieldNamed {
		attrs,
		ident,
		colon_token,
		ty,
	} = field;

	let mut delegator: Option<attr_kw::delegator> = None;

	let mut new_attrs = Any::<InputAttribute<Meta>>::new();
	for InputAttribute { pound_token, inner } in attrs {
		let (bracket, meta) = inner.into_parts();

		match meta {
			VariantFieldMeta::Delegator(kw) => {
				assign_unique_or_panic!(delegator, kw);
			}
			VariantFieldMeta::Syn(meta) => {
				let new_attr = InputAttribute {
					pound_token,
					inner: Bracket::from((bracket, meta)),
				};
				new_attrs.push(new_attr);
			}
		}
	}

	let new_field = VariantFieldNamed {
		attrs: new_attrs,
		ident,
		colon_token,
		ty,
	};

	Ok((new_field, delegator))
}

fn sanitize_field_unnamed(
	field: VariantFieldUnnamed<VariantFieldMeta>,
) -> Result<(VariantFieldUnnamed<Meta>, Option<attr_kw::delegator>)> {
	let VariantFieldUnnamed { attrs, ty } = field;

	let mut delegator: Option<attr_kw::delegator> = None;

	let mut new_attrs = Any::<InputAttribute<Meta>>::new();
	for InputAttribute { pound_token, inner } in attrs {
		let (bracket, meta) = inner.into_parts();

		match meta {
			VariantFieldMeta::Delegator(kw) => {
				assign_unique_or_panic!(delegator, kw);
			}
			VariantFieldMeta::Syn(meta) => {
				let new_attr = InputAttribute {
					pound_token,
					inner: Bracket::from((bracket, meta)),
				};
				new_attrs.push(new_attr);
			}
		}
	}

	let new_field = VariantFieldUnnamed {
		attrs: new_attrs,
		ty,
	};

	Ok((new_field, delegator))
}

impl ToTokens for SaneVariantFieldsNamed {
	fn to_tokens(&self, tokens: &mut TokenStream) { self.fields.to_tokens(tokens); }
}

impl ToTokens for SaneVariantFieldsUnnamed {
	fn to_tokens(&self, tokens: &mut TokenStream) { self.fields.to_tokens(tokens); }
}