eventstore_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    parse::{Parse, ParseStream},
5    parse_macro_input, AttrStyle, Attribute, FieldsNamed, Ident, Token, Visibility,
6};
7
8struct Structure {
9    attrs: Vec<Attribute>,
10    visibility: Visibility,
11    _struct_token: Token![struct],
12    name: Ident,
13    fields: FieldsNamed,
14}
15
16impl Parse for Structure {
17    fn parse(input: ParseStream) -> syn::Result<Self> {
18        Ok(Structure {
19            attrs: input.call(Attribute::parse_outer)?,
20            visibility: input.parse()?,
21            _struct_token: input.parse()?,
22            name: input.parse()?,
23            fields: input.parse()?,
24        })
25    }
26}
27
28fn attr_has_streaming_name(attr: &Attribute) -> bool {
29    attr.style == AttrStyle::Outer && attr.path.is_ident("streaming")
30}
31
32#[proc_macro_attribute]
33pub fn streaming(_attr: TokenStream, item: TokenStream) -> TokenStream {
34    item
35}
36
37#[proc_macro]
38pub fn options(input: TokenStream) -> TokenStream {
39    let input = parse_macro_input!(input as Structure);
40
41    let fields = input.fields.named.iter().map(|field| {
42        let attrs = if field.attrs.is_empty() {
43            quote! {}
44        } else {
45            let attrs = field.attrs.iter().map(|attr| quote! { #attr });
46            quote! {
47                #(#attrs)*
48            }
49        };
50
51        let vis = &field.vis;
52        let name = &field.ident;
53        let ty = &field.ty;
54
55        quote! {
56            #attrs
57            #vis #name: #ty,
58        }
59    });
60
61    let is_streaming = input.attrs.iter().any(attr_has_streaming_name);
62
63    let kind = if is_streaming {
64        quote! {
65            crate::options::OperationKind::Streaming
66        }
67    } else {
68        quote! {
69            crate::options::OperationKind::Regular
70        }
71    };
72
73    let attrs = input.attrs.iter().map(|attr| quote! { #attr });
74    let vis = &input.visibility;
75    let name = input.name;
76
77    TokenStream::from(quote! {
78        #(#attrs)*
79        #vis struct #name {
80            #(#fields)*
81            pub(crate) common_operation_options: crate::options::CommonOperationOptions,
82        }
83
84        impl crate::options::Options for #name {
85            fn common_operation_options(&self) -> &crate::options::CommonOperationOptions {
86                &self.common_operation_options
87            }
88
89            fn kind(&self) -> crate::options::OperationKind {
90                #kind
91            }
92        }
93
94        impl #name {
95            /// Performs the command with the given credentials.
96            pub fn authenticated(mut self, credentials: crate::types::Credentials) -> Self {
97                self.common_operation_options.credentials = Some(credentials);
98                self
99            }
100
101            pub fn requires_leader(mut self, requires_leader: bool) -> Self {
102                self.common_operation_options.requires_leader = requires_leader;
103                self
104            }
105
106            pub fn deadline(mut self, deadline: std::time::Duration) -> Self {
107                self.common_operation_options.deadline = Some(deadline);
108                self
109            }
110        }
111    })
112}