redis_module_ext_macros/
redis_data_type.rs

1use darling::FromMeta;
2use darling::ast::NestedMeta;
3use proc_macro2::TokenStream;
4use syn::spanned::Spanned;
5
6use crate::utils::SetSpan;
7
8#[derive(darling::FromMeta)]
9struct Args {
10    name: syn::LitStr,
11    version: syn::Expr,
12    methods: DataTypeMethods,
13}
14
15enum SetOrPath {
16    Set(syn::Path),
17    Path(syn::Path),
18}
19
20impl SetOrPath {
21    fn path(&self, name: &syn::Ident, generics: &syn::Generics) -> syn::Path {
22        match self {
23            Self::Set(func) => {
24                let ty = generics.type_params();
25                let path: syn::Path = syn::parse_quote! { #name::<#(#ty),*>::#func };
26                path.set_span(func.span())
27            }
28            Self::Path(path) => path.clone(),
29        }
30    }
31}
32
33impl darling::FromMeta for SetOrPath {
34    fn from_meta(item: &syn::Meta) -> darling::Result<Self> {
35        match item {
36            syn::Meta::Path(path) => Ok(Self::Set(path.clone())),
37            syn::Meta::NameValue(value) => {
38                let expr = &value.value;
39                Ok(Self::Path(syn::parse_quote!(#expr)))
40            }
41            _ => todo!(),
42        }
43    }
44}
45
46#[derive(darling::FromMeta)]
47struct DataTypeMethods {
48    rdb_load: SetOrPath,
49    rdb_save: SetOrPath,
50    aof_rewrite: Option<SetOrPath>,
51    mem_usage: Option<SetOrPath>,
52    digest: Option<SetOrPath>,
53    aux_load: Option<SetOrPath>,
54    aux_save: Option<SetOrPath>,
55    free_effort: Option<SetOrPath>,
56    unlink: Option<SetOrPath>,
57    copy: Option<SetOrPath>,
58    defrag: Option<SetOrPath>,
59    mem_usage2: Option<SetOrPath>,
60    free_effort2: Option<SetOrPath>,
61    unlink2: Option<SetOrPath>,
62    copy2: Option<SetOrPath>,
63    aux_save2: Option<SetOrPath>,
64}
65
66macro_rules! func_to_tokens {
67    (
68        $fn_ident:ident
69        $trait_ident:ident
70        $fn_ty:ty
71    ) => {
72        fn $fn_ident(&self, name: &syn::Ident, generics: &syn::Generics) -> TokenStream {
73            func_to_tokens!(@body; name, generics, self.$fn_ident, $trait_ident, $fn_ty)
74        }
75    };
76    (
77        opt $fn_ident:ident
78        $trait_ident:ident
79        $fn_ty:ty
80    ) => {
81        fn $fn_ident(&self, name: &syn::Ident, generics: &syn::Generics) -> TokenStream {
82            match &self.$fn_ident {
83                Some(value) => func_to_tokens!(@body; name, generics, value, $trait_ident, $fn_ty),
84                None => quote::quote!(::core::option::Option::None)
85            }
86        }
87    };
88    (
89        @body; $name:ident, $generics:ident, $path:expr, $trait_ident:ident, $fn_ty:ty
90    ) => {{
91        let (imp, ty, wh) = $generics.split_for_impl();
92        let path = $path.path($name, $generics);
93        quote::quote!({
94            struct Tmp;
95            impl #imp redis_module_ext::data_type::$trait_ident<Tmp> for #$name #ty #wh {
96                const FN: redis_module_ext::data_type::$fn_ty = #path;
97            }
98
99            <#$name #ty as redis_module_ext::data_type::$trait_ident<Tmp>>::extern_fn()
100        })
101    }}
102}
103
104impl DataTypeMethods {
105    func_to_tokens!(rdb_load RdbLoad RdbLoadFn<Self>);
106
107    func_to_tokens!(rdb_save RdbSave RdbSaveFn<Self>);
108
109    func_to_tokens!(opt aof_rewrite AofRewrite AofRewriteFn<Self>);
110
111    func_to_tokens!(opt mem_usage MemUsage MemUsageFn<Self>);
112
113    func_to_tokens!(opt digest Digest DigestFn<Self>);
114
115    func_to_tokens!(opt aux_load AuxLoad AuxLoadFn);
116
117    func_to_tokens!(opt aux_save AuxSave AuxSaveFn);
118
119    func_to_tokens!(opt free_effort FreeEffort FreeEffortFn<Self>);
120
121    func_to_tokens!(opt unlink Unlink UnlinkFn<Self>);
122
123    func_to_tokens!(opt copy Copy CopyFn<Self>);
124
125    func_to_tokens!(opt defrag Defrag DefragFn<Self>);
126
127    func_to_tokens!(opt mem_usage2 MemUsage2 MemUsage2Fn<Self>);
128
129    func_to_tokens!(opt free_effort2 FreeEffort2 FreeEffort2Fn<Self>);
130
131    func_to_tokens!(opt unlink2 Unlink2 Unlink2Fn<Self>);
132
133    func_to_tokens!(opt copy2 Copy2 Copy2Fn<Self>);
134
135    func_to_tokens!(opt aux_save2 AuxSave2 AuxSave2Fn);
136}
137
138pub fn macro_impl(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
139    let attr_args = NestedMeta::parse_meta_list(attr)?;
140
141    let args = Args::from_list(&attr_args)?;
142
143    if args.name.value().len() != 9 {
144        return Err(syn::Error::new(
145            args.name.span(),
146            format!("names must be exactly 9 characters long, found: {}", args.name.value().len()),
147        ));
148    }
149
150    let item = syn::parse2::<syn::Item>(item)?;
151
152    let (name, generics) = match &item {
153        syn::Item::Struct(item) => (&item.ident, &item.generics),
154        syn::Item::Enum(item) => (&item.ident, &item.generics),
155        syn::Item::Type(item) => (&item.ident, &item.generics),
156        syn::Item::Union(item) => (&item.ident, &item.generics),
157        _ => {
158            return Err(syn::Error::new_spanned(
159                item,
160                "data type can only be made from a struct / enum / type / union",
161            ));
162        }
163    };
164
165    let type_name = &args.name;
166    let type_version = &args.version;
167
168    let rdb_load = args.methods.rdb_load(name, generics);
169    let rdb_save = args.methods.rdb_save(name, generics);
170    let aof_rewrite = args.methods.aof_rewrite(name, generics);
171    let mem_usage = args.methods.mem_usage(name, generics);
172    let digest = args.methods.digest(name, generics);
173    let aux_load = args.methods.aux_load(name, generics);
174    let aux_save = args.methods.aux_save(name, generics);
175    let free_effort = args.methods.free_effort(name, generics);
176    let unlink = args.methods.unlink(name, generics);
177    let copy = args.methods.copy(name, generics);
178    let defrag = args.methods.defrag(name, generics);
179    let mem_usage2 = args.methods.mem_usage2(name, generics);
180    let free_effort2 = args.methods.free_effort2(name, generics);
181    let unlink2 = args.methods.unlink2(name, generics);
182    let copy2 = args.methods.copy2(name, generics);
183    let aux_save2 = args.methods.aux_save2(name, generics);
184
185    let (imp, ty, wh) = generics.split_for_impl();
186
187    Ok(quote::quote! {
188        #item
189
190        const _: () = {
191            impl #imp redis_module_ext::data_type::RedisDataType for #name #ty #wh {
192                const NAME: &'static str = #type_name;
193                const VERSION: i32 = #type_version;
194
195                fn module_methods(_: &::redis_module_ext::redis::Context) -> redis_module_ext::raw::RedisModuleTypeMethods {
196                    redis_module_ext::raw::RedisModuleTypeMethods {
197                        version: redis_module_ext::raw::REDISMODULE_TYPE_METHOD_VERSION as u64,
198                        aux_save_triggers: 0,
199                        rdb_load: #rdb_load,
200                        rdb_save: #rdb_save,
201                        aof_rewrite: #aof_rewrite,
202                        mem_usage: #mem_usage,
203                        digest: #digest,
204                        free: {
205                            unsafe extern "C" fn free<T>(value: *mut ::std::os::raw::c_void) {
206                                drop(unsafe { ::std::boxed::Box::from_raw(value.cast::<T>()) });
207                            }
208
209                            Some(free::<#name #ty>)
210                        },
211                        aux_load: #aux_load,
212                        aux_save: #aux_save,
213                        free_effort: #free_effort,
214                        unlink: #unlink,
215                        copy: #copy,
216                        defrag: #defrag,
217                        mem_usage2: #mem_usage2,
218                        free_effort2: #free_effort2,
219                        unlink2: #unlink2,
220                        copy2: #copy2,
221                        aux_save2: #aux_save2,
222                    }
223                }
224            }
225        };
226    })
227}