ruleset-macros 1.0.0

This crate shouldn't be used directly. Check `ruleset`.
Documentation
mod antecedent;
mod consequent;
mod replacements;
mod ruleset;
mod subrule;

use crate::ruleset::Ruleset;
use crate::subrule::Subrule;
use convert_case::Case;
use convert_case::Casing;
use in_definite::get_a_or_an;
use proc_macro::TokenStream;
use proc_macro_error::abort;
use proc_macro_error::proc_macro_error;
use quote::quote;
use syn::parse_macro_input;
use syn::parse_str;
use syn::Item;
use syn::ItemEnum;

#[proc_macro_error]
#[proc_macro]
pub fn ruleset(input: TokenStream) -> TokenStream {
	let ruleset = parse_macro_input!(input as Ruleset);
	ruleset.expand().into()
}

#[doc(hidden)]
#[proc_macro_error]
#[proc_macro_derive(AsRef)]
pub fn as_ref(input: TokenStream) -> TokenStream {
	let item = parse_macro_input!(input as Item);
	let (r#type, generics) = match item {
		Item::Enum(r#enum) => (r#enum.ident.clone(), r#enum.generics.clone()),
		Item::Struct(r#struct) => (r#struct.ident.clone(), r#struct.generics.clone()),
		_ => abort!(item, "only `struct`s and `enum`s are supported by `AsRef`"),
	};
	let (generics_impl, generics_type, where_clause) = generics.split_for_impl();
	quote! {
		impl #generics_impl std::convert::AsRef<#r#type #generics_type> for #r#type #generics_type #where_clause {
			fn as_ref(&self) -> &Self { self }
		}
	}
	.into()
}

#[doc(hidden)]
#[proc_macro_error]
#[proc_macro_derive(Enum)]
pub fn r#enum(input: TokenStream) -> TokenStream {
	let r#enum = parse_macro_input!(input as ItemEnum);
	let r#type = &r#enum.ident;
	let (generics_impl, generics_type, where_clause) = &r#enum.generics.split_for_impl();
	let impls = r#enum.variants.iter().map(|variant| {
		let ident = &variant.ident;
		if variant.fields.len() != 1 {
			abort!(variant.fields, "only 1-field variants are supported by `Enum`");
		}
		let field = variant.fields.iter().nth(0).unwrap();
		let name = &ident.to_string().to_case(Case::Lower);
		let article = get_a_or_an(name);

		let fn_name = parse_str::<syn::Ident>(&ident.to_string().to_case(Case::Snake)).unwrap();
		let doc = format!(" Returns a reference to the inner value if this is {article} {name}.");

		let fn_name_is =
			parse_str::<syn::Ident>(&format!("is_{}", &ident.to_string().to_case(Case::Snake)))
				.unwrap();
		let doc_is = format!(" Returns whether this is {article} {name}.");

		let fn_name_into =
			parse_str::<syn::Ident>(&format!("into_{}", &ident.to_string().to_case(Case::Snake)))
				.unwrap();
		let doc_into = format!(" Returns the inner value if this is {article} {name}.");

		quote! {
			impl #generics_impl #r#type #generics_type #where_clause {
				#[doc = #doc]
				pub fn #fn_name(&self) -> std::option::Option<&#field> {
					match self {
						Self::#ident(value) => std::option::Option::Some(value),
						_ => std::option::Option::None,
					}
				}
				#[doc = #doc_is]
				pub fn #fn_name_is(&self) -> bool {
					self.#fn_name().is_some()
				}
				#[doc = #doc_into]
				pub fn #fn_name_into(self) -> std::option::Option<#field> {
					match self {
						Self::#ident(value) => std::option::Option::Some(value),
						_ => std::option::Option::None,
					}
				}
			}
		}
	});
	quote! {
		#(#impls)*
	}
	.into()
}