csskit_derives 0.0.19

Derive macros for CSS parsing, visiting, and AST operations.
Documentation
use crate::{WhereCollector, err};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Data, DataEnum, DataStruct, DeriveInput, Fields, Index, parse_quote};

pub fn derive(input: DeriveInput) -> TokenStream {
	let mut where_collector = WhereCollector::new();
	let ident = input.ident;
	let generics = input.generics.clone();
	let (impl_generics, type_generics, _) = generics.split_for_impl();
	let body = match input.data {
		Data::Struct(DataStruct { fields: Fields::Unnamed(fields), .. }) => {
			let steps: Vec<TokenStream> = fields
				.unnamed
				.into_iter()
				.enumerate()
				.map(|(i, field)| {
					let index = Index { index: i as u32, span: Span::call_site() };
					where_collector.add(&field.ty);
					quote! {
						ToCursors::to_cursors(&self.#index, s);
					}
				})
				.collect();
			quote! { #(#steps)* }
		}

		Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => {
			let steps: Vec<TokenStream> = fields
				.named
				.into_iter()
				.map(|f| {
					let ident = f.ident.expect("Named field");
					where_collector.add(&f.ty);
					quote! {
						ToCursors::to_cursors(&self.#ident, s);
					}
				})
				.collect();
			quote! { #(#steps)* }
		}

		Data::Struct(_) => err(ident.span(), "Cannot derive ToCursors on this struct"),

		Data::Union(_) => err(ident.span(), "Cannot derive ToCursors on a Union"),

		Data::Enum(DataEnum { variants, .. }) => {
			let mut steps = vec![];
			for var in variants {
				let var_ident = var.ident;
				let mut idents = vec![];
				let field_steps: Vec<_> = var
					.fields
					.into_iter()
					.enumerate()
					.map(|(i, field)| {
						where_collector.add(&field.ty);
						let ident = format_ident!("v{}", i);
						idents.push(ident.clone());
						quote! { ToCursors::to_cursors(#ident, s); }
					})
					.collect();
				steps.push(quote! {
					Self::#var_ident(#(#idents),*) => { #(#field_steps)* }
				});
			}
			quote! {
				match self {
					#(#steps)*
				}
			}
		}
	};

	let mut generics = input.generics.clone();
	let where_clause = where_collector.extend_where_clause(&mut generics, parse_quote! { ::css_parse::ToCursors });

	quote! {
		#[automatically_derived]
		impl #impl_generics ::css_parse::ToCursors for #ident #type_generics #where_clause {
			fn to_cursors(&self, s: &mut impl ::css_parse::CursorSink) {
				use ::css_parse::ToCursors;
				#body
			}
		}
	}
}