redis_module_ext_macros/
redis_module.rs1use 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}