Skip to main content

hegel_macros/
lib.rs

1mod common;
2mod composite;
3mod enum_gen;
4mod explicit_test_case;
5mod hegel_main;
6mod hegel_test;
7mod reproduce_failure;
8mod rewrite_draws;
9mod standalone_function;
10mod stateful;
11mod struct_gen;
12mod utils;
13
14use proc_macro::TokenStream;
15use syn::{Data, DeriveInput, ItemFn, ItemImpl, parse_macro_input};
16
17#[proc_macro_derive(DefaultGenerator)]
18pub fn derive_generator(input: TokenStream) -> TokenStream {
19    let input = parse_macro_input!(input as DeriveInput);
20
21    match &input.data {
22        Data::Struct(data) => struct_gen::derive_struct_generator(&input, data),
23        Data::Enum(data) => enum_gen::derive_enum_generator(&input, data),
24        Data::Union(_) => syn::Error::new_spanned(&input, "Generator cannot be derived for unions")
25            .to_compile_error()
26            .into(),
27    }
28}
29
30#[proc_macro_attribute]
31pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
32    hegel_test::expand_test(attr.into(), item.into()).into()
33}
34
35#[proc_macro_attribute]
36pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
37    hegel_main::expand_main(attr.into(), item.into()).into()
38}
39
40#[proc_macro_attribute]
41pub fn standalone_function(attr: TokenStream, item: TokenStream) -> TokenStream {
42    standalone_function::expand_standalone_function(attr.into(), item.into()).into()
43}
44
45#[proc_macro_attribute]
46pub fn composite(attr: TokenStream, item: TokenStream) -> TokenStream {
47    if !attr.is_empty() {
48        return syn::Error::new_spanned(
49            proc_macro2::TokenStream::from(attr),
50            "#[hegel::composite] takes no arguments",
51        )
52        .to_compile_error()
53        .into();
54    }
55    let input = parse_macro_input!(item as ItemFn);
56    composite::expand_composite(input).into()
57}
58
59/// Define an explicit test case to run before the property-based test.
60///
61/// Note: We are not currently 100% happy with the name of this attribute
62/// and expect that we might change it in future. The API should otherwise
63/// remain compatible, but you might have to rename some call-sites.
64///
65/// Must be placed **below** `#[hegel::test]`. Multiple attributes are allowed.
66///
67/// ```ignore
68/// #[hegel::test]
69/// #[hegel::explicit_test_case(x = 42, y = "hello")]
70/// fn my_test(tc: hegel::TestCase) {
71///     let x: i32 = tc.draw(hegel::generators::integers());
72///     let y: String = tc.draw(hegel::generators::text());
73///     // ...
74/// }
75/// ```
76///
77/// Arguments correspond to the names they would be printed with in a failing
78/// test case, so need suffixing if they're repeated. For example:
79///
80/// ```ignore
81/// #[hegel::test]
82/// #[hegel::explicit_test_case(x_1 = 1, x_2 = 2, x_3 = 4 )]
83/// fn my_test(tc: hegel::TestCase) {
84///     for _ in 0..3 {
85///         let x: i32 = tc.draw(hegel::generators::integers());
86///     }
87/// }
88/// ```
89#[proc_macro_attribute]
90pub fn explicit_test_case(attr: TokenStream, item: TokenStream) -> TokenStream {
91    explicit_test_case::expand_explicit_test_case(attr.into(), item.into()).into()
92}
93
94/// Reproduce a single failing example from a base64 failure blob.
95///
96/// Documentation lives in hegel's lib.rs for intra-doc links.
97#[proc_macro_attribute]
98pub fn reproduce_failure(attr: TokenStream, item: TokenStream) -> TokenStream {
99    reproduce_failure::expand_reproduce_failure(attr.into(), item.into()).into()
100}
101
102#[proc_macro_attribute]
103pub fn state_machine(attr: TokenStream, item: TokenStream) -> TokenStream {
104    if !attr.is_empty() {
105        return syn::Error::new_spanned(
106            proc_macro2::TokenStream::from(attr),
107            "#[hegel::state_machine] takes no arguments",
108        )
109        .to_compile_error()
110        .into();
111    }
112    let block = parse_macro_input!(item as ItemImpl);
113    stateful::expand_state_machine(block).into()
114}
115
116/// Rewrite `tc.draw(gen)` calls inside a closure body to the named form used
117/// by `#[hegel::test]`, so failing-test output prints real variable names.
118///
119/// Intended for test infrastructure that constructs `Hegel::new(...)` by hand
120/// (e.g. when wrapping a test run with custom output capture) where the
121/// ordinary `#[hegel::test]` attribute isn't an option.
122///
123/// ```ignore
124/// let closure = hegel::rewrite_draws!(|tc: hegel::TestCase| {
125///     let x: i32 = tc.draw(hegel::generators::integers());
126///     assert!(x < 10);
127/// });
128/// ```
129#[proc_macro]
130pub fn rewrite_draws(input: TokenStream) -> TokenStream {
131    rewrite_draws::expand_rewrite_draws(input.into()).into()
132}