csskit_proc_macro 0.0.24

Procedural macros for generating CSS parsers from grammar specifications.
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Meta, Token};

use crate::generate::*;
use css_value_definition_parser::*;

fn has_derive_of(attrs: &[Attribute], name: &'static str) -> bool {
	for attr in attrs {
		if attr.path().is_ident("derive") {
			let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated).unwrap_or_default();
			for meta in nested {
				match meta {
					Meta::Path(path)
						if path.is_ident(name) || path.segments.last().map(|s| s.ident == name).unwrap_or(false) =>
					{
						return true;
					}
					_ => {}
				}
			}
		}
	}
	false
}

pub fn generate(defs: Def, ast: DeriveInput) -> TokenStream {
	let has_a_lifetime = ast.generics.lifetimes().any(|l| l.lifetime.ident == "a");
	if !has_a_lifetime && defs.maybe_unsized() {
		return Error::new(ast.ident.span(), "this object needs the <'a> lifetime but it didn't have it. Add it")
			.into_compile_error();
	}
	let vis = &ast.vis;
	let attrs = &ast.attrs;
	let ident = &ast.ident;
	match &ast.data {
		Data::Enum(DataEnum { variants, .. }) => {
			if !variants.is_empty() {
				return Error::new(ident.span(), "enum must be empty").into_compile_error();
			}
			if !defs.suggested_data_type().is_enum() {
				return Error::new(ident.span(), "wrong structure for this syntax, please redefine as a Struct")
					.into_compile_error();
			}
		}
		Data::Struct(DataStruct { fields, .. }) => {
			if !fields.is_empty() {
				return Error::new(ident.span(), "struct must be empty").into_compile_error();
			}
			if !defs.suggested_data_type().is_struct() {
				return Error::new(ident.span(), "wrong structure for this syntax, please redefine as an Enum")
					.into_compile_error();
			}
		}
		Data::Union(_) => {
			return Error::new(ident.span(), "cannot create from_syntax on Union").into_compile_error();
		}
	}
	let derives_visitable = has_derive_of(attrs, "Visitable");
	let derives_parse = has_derive_of(attrs, "Parse");
	let additonal_defs = defs.generate_additional_types(vis, ident, &ast.generics);
	let def = defs.generate_definition(vis, ident, &ast.generics, derives_parse, derives_visitable);
	quote! {
		#additonal_defs

		#(#attrs)*
		#def
	}
}