ruleset_macros/
lib.rs

1mod antecedent;
2mod consequent;
3mod replacements;
4mod ruleset;
5mod subrule;
6
7use crate::ruleset::Ruleset;
8use crate::subrule::Subrule;
9use convert_case::Case;
10use convert_case::Casing;
11use in_definite::get_a_or_an;
12use proc_macro::TokenStream;
13use proc_macro_error::abort;
14use proc_macro_error::proc_macro_error;
15use quote::quote;
16use syn::parse_macro_input;
17use syn::parse_str;
18use syn::Item;
19use syn::ItemEnum;
20
21#[proc_macro_error]
22#[proc_macro]
23pub fn ruleset(input: TokenStream) -> TokenStream {
24	let ruleset = parse_macro_input!(input as Ruleset);
25	ruleset.expand().into()
26}
27
28#[doc(hidden)]
29#[proc_macro_error]
30#[proc_macro_derive(AsRef)]
31pub fn as_ref(input: TokenStream) -> TokenStream {
32	let item = parse_macro_input!(input as Item);
33	let (r#type, generics) = match item {
34		Item::Enum(r#enum) => (r#enum.ident.clone(), r#enum.generics.clone()),
35		Item::Struct(r#struct) => (r#struct.ident.clone(), r#struct.generics.clone()),
36		_ => abort!(item, "only `struct`s and `enum`s are supported by `AsRef`"),
37	};
38	let (generics_impl, generics_type, where_clause) = generics.split_for_impl();
39	quote! {
40		impl #generics_impl std::convert::AsRef<#r#type #generics_type> for #r#type #generics_type #where_clause {
41			fn as_ref(&self) -> &Self { self }
42		}
43	}
44	.into()
45}
46
47#[doc(hidden)]
48#[proc_macro_error]
49#[proc_macro_derive(Enum)]
50pub fn r#enum(input: TokenStream) -> TokenStream {
51	let r#enum = parse_macro_input!(input as ItemEnum);
52	let r#type = &r#enum.ident;
53	let (generics_impl, generics_type, where_clause) = &r#enum.generics.split_for_impl();
54	let impls = r#enum.variants.iter().map(|variant| {
55		let ident = &variant.ident;
56		if variant.fields.len() != 1 {
57			abort!(variant.fields, "only 1-field variants are supported by `Enum`");
58		}
59		let field = variant.fields.iter().nth(0).unwrap();
60		let name = &ident.to_string().to_case(Case::Lower);
61		let article = get_a_or_an(name);
62
63		let fn_name = parse_str::<syn::Ident>(&ident.to_string().to_case(Case::Snake)).unwrap();
64		let doc = format!(" Returns a reference to the inner value if this is {article} {name}.");
65
66		let fn_name_is =
67			parse_str::<syn::Ident>(&format!("is_{}", &ident.to_string().to_case(Case::Snake)))
68				.unwrap();
69		let doc_is = format!(" Returns whether this is {article} {name}.");
70
71		let fn_name_into =
72			parse_str::<syn::Ident>(&format!("into_{}", &ident.to_string().to_case(Case::Snake)))
73				.unwrap();
74		let doc_into = format!(" Returns the inner value if this is {article} {name}.");
75
76		quote! {
77			impl #generics_impl #r#type #generics_type #where_clause {
78				#[doc = #doc]
79				pub fn #fn_name(&self) -> std::option::Option<&#field> {
80					match self {
81						Self::#ident(value) => std::option::Option::Some(value),
82						_ => std::option::Option::None,
83					}
84				}
85				#[doc = #doc_is]
86				pub fn #fn_name_is(&self) -> bool {
87					self.#fn_name().is_some()
88				}
89				#[doc = #doc_into]
90				pub fn #fn_name_into(self) -> std::option::Option<#field> {
91					match self {
92						Self::#ident(value) => std::option::Option::Some(value),
93						_ => std::option::Option::None,
94					}
95				}
96			}
97		}
98	});
99	quote! {
100		#(#impls)*
101	}
102	.into()
103}