anytest_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use regex::{Captures, Regex};
4use syn::{parse_macro_input, DeriveInput};
5
6fn to_snake_case(value: String) -> String {
7    let re1 = Regex::new(r"([A-Z]+)([A-Z][a-z])").unwrap();
8    let re2 = Regex::new(r"([a-z]|\d)([A-Z])").unwrap();
9
10    let replaced1 = re1.replace_all(&value, |caps: &Captures| {
11        format!("{}_{}", &caps[1], &caps[2])
12    });
13
14    let replaced2 = re2.replace_all(&replaced1, |caps: &Captures| {
15        format!("{}_{}", &caps[1], &caps[2])
16    });
17
18    replaced2.to_lowercase()
19}
20
21#[proc_macro_derive(Language)]
22pub fn derive_language(input: TokenStream) -> TokenStream {
23    let input = parse_macro_input!(input as DeriveInput);
24    let language = input.ident;
25    let language_name = format_ident!("{}", to_snake_case(language.to_string()));
26
27    let expanded = quote! {
28        impl Language for #language {
29            fn name(&self) -> &str {
30                stringify!(#language_name)
31            }
32        }
33    };
34
35    TokenStream::from(expanded)
36}
37
38#[proc_macro_derive(TestFrameworkMeta)]
39pub fn derive_test_framework_meta(input: TokenStream) -> TokenStream {
40    let input = parse_macro_input!(input as DeriveInput);
41    let test_framework = input.ident;
42    let test_framework_name = format_ident!("{}", to_snake_case(test_framework.to_string()));
43
44    let expanded = quote! {
45        impl TestFrameworkMeta for #test_framework {
46            fn language(&self) -> &dyn crate::language::Language {
47                &self.language
48            }
49
50            fn name(&self) -> &str {
51                stringify!(#test_framework_name)
52            }
53
54            fn pattern(&self) -> Result<regex::Regex, regex::Error> {
55                regex::Regex::new(&self.pattern)
56            }
57
58            fn default_executable(&self) -> Option<crate::ArgsList> {
59                if self.executable.is_empty() {
60                    None
61                } else {
62                    Some(self.executable.clone().into_iter().map(|s| s.to_string()).collect())
63                }
64            }
65
66            fn args(&self) -> crate::ArgsList {
67                self.args.clone().into_iter().map(|s| s.to_string()).collect()
68            }
69
70
71            fn test_pattern(&self) -> &str {
72                &self.test_pattern
73            }
74
75            fn namespace_pattern(&self) -> &str {
76                &self.namespace_pattern
77            }
78        }
79    };
80
81    TokenStream::from(expanded)
82}