redis_module_ext_macros/
redis_module.rs

1use darling::FromMeta;
2use darling::ast::NestedMeta;
3use proc_macro2::TokenStream;
4
5use crate::utils::{format_command_ident, repeated_parse, str_to_cstr};
6
7#[derive(darling::FromMeta, Debug)]
8struct Args {
9    name: syn::LitStr,
10    version: syn::LitInt,
11    #[darling(default, with = repeated_parse)]
12    types: Vec<syn::TypePath>,
13    #[darling(default, with = repeated_parse)]
14    commands: Vec<syn::TypePath>,
15    #[darling(default, with = repeated_parse)]
16    merge: Vec<syn::TypePath>,
17    #[darling(default)]
18    init_fn: Option<syn::Path>,
19    #[darling(default)]
20    deinit_fn: Option<syn::Path>,
21}
22
23pub fn macro_impl(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
24    let attr_args = NestedMeta::parse_meta_list(attr)?;
25
26    let args = Args::from_list(&attr_args)?;
27
28    let item = syn::parse2(item)?;
29
30    let (name, generics) = match &item {
31        syn::Item::Struct(item) => (&item.ident, &item.generics),
32        syn::Item::Enum(item) => (&item.ident, &item.generics),
33        _ => {
34            return Err(syn::Error::new_spanned(
35                item,
36                "module can only be implemented on a struct or enum",
37            ));
38        }
39    };
40
41    let (imp, ty, wh) = generics.split_for_impl();
42
43    let mod_version = &args.version;
44    let mod_name = str_to_cstr(&args.name)?;
45
46    let data_types = args.types.iter().map(|ty| {
47        quote::quote! {
48            if <#ty as redis_module_ext::RedisDataType>::register(ctx).is_err() {
49                return false;
50            }
51        }
52    });
53
54    let merge_types = &args.merge;
55
56    let commands = args.commands.iter().map(|cmd| {
57        let mut cmd_path = cmd.path.clone();
58        let mut path = cmd_path.clone();
59        let last = path.segments.last_mut().unwrap();
60        last.ident = format_command_ident(&last.ident);
61
62        let last = cmd_path.segments.last_mut().unwrap();
63        if let syn::PathArguments::AngleBracketed(args) = &mut last.arguments {
64            args.colon2_token = Some(Default::default());
65            let generics = args
66                .args
67                .clone()
68                .into_pairs()
69                .filter(|generic| {
70                    matches!(
71                        generic.value(),
72                        syn::GenericArgument::Const(_) | syn::GenericArgument::Type(_)
73                    )
74                })
75                .collect();
76            args.args = generics;
77        }
78
79        quote::quote! {
80            let _ = #cmd_path;
81            <#path as redis_module_ext::RedisCommand>::register(ctx)?;
82        }
83    });
84
85    let init_fn = args.init_fn.map(|init_fn| quote::quote!(#init_fn(self, ctx)?));
86    let deinit_fn = args.deinit_fn.map(|deinit_fn| quote::quote!(#deinit_fn(self, ctx)?));
87
88    Ok(quote::quote! {
89        #item
90
91        impl #imp redis_module_ext::module::RedisModule for #name #ty #wh {
92            fn name() -> &'static ::std::ffi::CStr {
93                #mod_name
94            }
95
96            fn version() -> i32 {
97                #mod_version
98            }
99
100            fn register_data_types(ctx: &::redis_module_ext::redis::Context) -> bool {
101                #(#data_types)*
102                true #(
103                    && #merge_types::register_data_types(ctx)
104                )*
105            }
106
107            fn register_commands(ctx: &::redis_module_ext::redis::Context) -> ::redis_module_ext::redis::RedisResult<()> {
108                #(#commands)*
109                #(#merge_types::register_commands(ctx)?;)*
110                ::std::result::Result::Ok(())
111            }
112
113            fn init_fn(ctx: &::redis_module_ext::redis::Context) -> ::redis_module_ext::redis::RedisResult<()> {
114                #init_fn
115                #(#merge_types::init_fn(ctx)?;)*
116                ::std::result::Result::Ok(())
117            }
118
119            fn deinit_fn(ctx: &::redis_module_ext::redis::Context) -> ::redis_module_ext::redis::RedisResult<()> {
120                #deinit_fn
121                #(#merge_types::deinit_fn(ctx)?;)*
122                ::std::result::Result::Ok(())
123            }
124        }
125    })
126}