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