1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream {
    let f = syn::parse_macro_input!(fun as syn::ItemFn);

    assert!(
        f.attrs.is_empty(),
        "netsim_embed::machine cannot have any attribute"
    );
    assert!(
        f.sig.constness.is_none(),
        "netsim_embed::machine cannot be const"
    );
    assert!(
        f.sig.asyncness.is_none(),
        "netsim_embed::machine cannot be async"
    );
    assert!(
        f.sig.unsafety.is_none(),
        "netsim_embed::machine cannot be unsafe"
    );
    assert!(
        f.sig.abi.is_none(),
        "netsim_embed::machine cannot have an abi defined"
    );
    assert!(
        f.sig.generics.params.is_empty(),
        "netsim_embed::machine cannot be generic"
    );
    assert!(
        f.sig.inputs.len() == 1,
        "netsim_embed::machine must take exactly one argument"
    );
    assert!(
        f.sig.variadic.is_none(),
        "netsim_embed::machine cannot be variadic"
    );
    assert!(
        matches!(f.sig.output, syn::ReturnType::Default),
        "netsim_embed::machine must not declare a return type"
    );

    let input = match &f.sig.inputs.first().unwrap() {
        syn::FnArg::Typed(input) => input,
        _ => panic!("netsim_embed::machine must be a freestanding function"),
    };
    assert!(
        input.attrs.is_empty(),
        "netsim_embed::machine's only argument must not have any attributes attached"
    );

    let f_vis = f.vis;
    let f_ident = f.sig.ident;
    let input_ty = &input.ty;
    let id: u128 = rand::random();
    let input_pat = &input.pat;
    let f_block = f.block;

    TokenStream::from(quote::quote! {
        #[allow(non_camel_case_types)]
        #f_vis struct #f_ident ;

        impl netsim_embed::MachineFn for #f_ident {
            type Arg = #input_ty ;

            fn id() -> u128 {
                #id
            }

            fn call(#input_pat: #input_ty) #f_block
        }
    })
}