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}