1 use libc::size_t;
2 use std::ffi::CString;
3 use std::os::raw::{c_double, c_int, c_longlong};
4 use std::ptr::{null, null_mut};
5 use std::{
6     ffi::CStr,
7     os::raw::{c_char, c_void},
8 };
9 
10 use crate::commands::KeyValue;
11 use jsonpath_lib::select::select_value::{SelectValue, SelectValueType};
12 use jsonpath_lib::select::Selector;
13 use redis_module::raw as rawmod;
14 use redis_module::{Context, RedisString, Status};
15 
16 use crate::manager::{Manager, ReadHolder};
17 
18 // extern crate readies_wd40;
19 // use crate::readies_wd40::{BB, _BB, getenv};
20 
21 //
22 // structs
23 //
24 
25 #[repr(C)]
26 pub enum JSONType {
27     String = 0,
28     Int = 1,
29     Double = 2,
30     Bool = 3,
31     Object = 4,
32     Array = 5,
33     Null = 6,
34 }
35 
36 struct ResultsIterator<'a, V: SelectValue> {
37     results: Vec<&'a V>,
38     pos: usize,
39 }
40 
41 //---------------------------------------------------------------------------------------------
42 
43 pub static mut LLAPI_CTX: Option<*mut rawmod::RedisModuleCtx> = None;
44 
45 #[allow(clippy::not_unsafe_ptr_arg_deref)]
create_rmstring( ctx: *mut rawmod::RedisModuleCtx, from_str: &str, str: *mut *mut rawmod::RedisModuleString, ) -> c_int46 pub fn create_rmstring(
47     ctx: *mut rawmod::RedisModuleCtx,
48     from_str: &str,
49     str: *mut *mut rawmod::RedisModuleString,
50 ) -> c_int {
51     if let Ok(s) = CString::new(from_str) {
52         let p = s.as_bytes_with_nul().as_ptr() as *const c_char;
53         let len = s.as_bytes().len();
54         unsafe { *str = rawmod::RedisModule_CreateString.unwrap()(ctx, p, len) };
55         return Status::Ok as c_int;
56     }
57     Status::Err as c_int
58 }
59 
json_api_open_key_internal<M: Manager>( manager: M, ctx: *mut rawmod::RedisModuleCtx, key: RedisString, ) -> *const M::V60 pub fn json_api_open_key_internal<M: Manager>(
61     manager: M,
62     ctx: *mut rawmod::RedisModuleCtx,
63     key: RedisString,
64 ) -> *const M::V {
65     let ctx = Context::new(ctx);
66     if let Ok(h) = manager.open_key_read(&ctx, &key) {
67         if let Ok(Some(v)) = h.get_value() {
68             return v;
69         }
70     }
71     null()
72 }
73 
json_api_get_at<M: Manager>(_: M, json: *const c_void, index: size_t) -> *const c_void74 pub fn json_api_get_at<M: Manager>(_: M, json: *const c_void, index: size_t) -> *const c_void {
75     let json = unsafe { &*(json as *const M::V) };
76     match json.get_type() {
77         SelectValueType::Array => match json.get_index(index) {
78             Some(v) => v as *const M::V as *const c_void,
79             _ => null(),
80         },
81         _ => null(),
82     }
83 }
84 
85 #[allow(clippy::not_unsafe_ptr_arg_deref)]
json_api_get_len<M: Manager>(_: M, json: *const c_void, count: *mut libc::size_t) -> c_int86 pub fn json_api_get_len<M: Manager>(_: M, json: *const c_void, count: *mut libc::size_t) -> c_int {
87     let json = unsafe { &*(json as *const M::V) };
88     let len = match json.get_type() {
89         SelectValueType::String => Some(json.get_str().len()),
90         SelectValueType::Array => Some(json.len().unwrap()),
91         SelectValueType::Object => Some(json.len().unwrap()),
92         _ => None,
93     };
94     match len {
95         Some(l) => {
96             unsafe { *count = l };
97             Status::Ok as c_int
98         }
99         None => Status::Err as c_int,
100     }
101 }
102 
json_api_get_type<M: Manager>(_: M, json: *const c_void) -> c_int103 pub fn json_api_get_type<M: Manager>(_: M, json: *const c_void) -> c_int {
104     json_api_get_type_internal(unsafe { &*(json as *const M::V) }) as c_int
105 }
106 
json_api_get_string<M: Manager>( _: M, json: *const c_void, str: *mut *const c_char, len: *mut size_t, ) -> c_int107 pub fn json_api_get_string<M: Manager>(
108     _: M,
109     json: *const c_void,
110     str: *mut *const c_char,
111     len: *mut size_t,
112 ) -> c_int {
113     let json = unsafe { &*(json as *const M::V) };
114     match json.get_type() {
115         SelectValueType::String => {
116             let s = json.as_str();
117             set_string(s, str, len);
118             Status::Ok as c_int
119         }
120         _ => Status::Err as c_int,
121     }
122 }
123 
json_api_get_json<M: Manager>( _: M, json: *const c_void, ctx: *mut rawmod::RedisModuleCtx, str: *mut *mut rawmod::RedisModuleString, ) -> c_int124 pub fn json_api_get_json<M: Manager>(
125     _: M,
126     json: *const c_void,
127     ctx: *mut rawmod::RedisModuleCtx,
128     str: *mut *mut rawmod::RedisModuleString,
129 ) -> c_int {
130     let json = unsafe { &*(json as *const M::V) };
131     let res = KeyValue::<M::V>::serialize_object(json, None, None, None);
132     create_rmstring(ctx, &res, str)
133 }
134 
135 #[allow(clippy::not_unsafe_ptr_arg_deref)]
json_api_get_int<M: Manager>(_: M, json: *const c_void, val: *mut c_longlong) -> c_int136 pub fn json_api_get_int<M: Manager>(_: M, json: *const c_void, val: *mut c_longlong) -> c_int {
137     let json = unsafe { &*(json as *const M::V) };
138     match json.get_type() {
139         SelectValueType::Long => {
140             unsafe { *val = json.get_long() };
141             Status::Ok as c_int
142         }
143         _ => Status::Err as c_int,
144     }
145 }
146 
147 #[allow(clippy::not_unsafe_ptr_arg_deref)]
json_api_get_double<M: Manager>(_: M, json: *const c_void, val: *mut c_double) -> c_int148 pub fn json_api_get_double<M: Manager>(_: M, json: *const c_void, val: *mut c_double) -> c_int {
149     let json = unsafe { &*(json as *const M::V) };
150     match json.get_type() {
151         SelectValueType::Double => {
152             unsafe { *val = json.get_double() };
153             Status::Ok as c_int
154         }
155         _ => Status::Err as c_int,
156     }
157 }
158 
159 #[allow(clippy::not_unsafe_ptr_arg_deref)]
json_api_get_boolean<M: Manager>(_: M, json: *const c_void, val: *mut c_int) -> c_int160 pub fn json_api_get_boolean<M: Manager>(_: M, json: *const c_void, val: *mut c_int) -> c_int {
161     let json = unsafe { &*(json as *const M::V) };
162     match json.get_type() {
163         SelectValueType::Bool => {
164             unsafe { *val = json.get_bool() as c_int };
165             Status::Ok as c_int
166         }
167         _ => Status::Err as c_int,
168     }
169 }
170 
171 //---------------------------------------------------------------------------------------------
172 
173 #[allow(clippy::not_unsafe_ptr_arg_deref)]
set_string(from_str: &str, str: *mut *const c_char, len: *mut size_t) -> c_int174 pub fn set_string(from_str: &str, str: *mut *const c_char, len: *mut size_t) -> c_int {
175     if !str.is_null() {
176         unsafe {
177             *str = from_str.as_ptr() as *const c_char;
178             *len = from_str.len();
179         }
180         return Status::Ok as c_int;
181     }
182     Status::Err as c_int
183 }
184 
json_api_get_type_internal<V: SelectValue>(v: &V) -> JSONType185 fn json_api_get_type_internal<V: SelectValue>(v: &V) -> JSONType {
186     match v.get_type() {
187         SelectValueType::Null => JSONType::Null,
188         SelectValueType::Bool => JSONType::Bool,
189         SelectValueType::Long => JSONType::Int,
190         SelectValueType::Double => JSONType::Double,
191         SelectValueType::String => JSONType::String,
192         SelectValueType::Array => JSONType::Array,
193         SelectValueType::Object => JSONType::Object,
194     }
195 }
196 
json_api_next<M: Manager>(_: M, iter: *mut c_void) -> *const c_void197 pub fn json_api_next<M: Manager>(_: M, iter: *mut c_void) -> *const c_void {
198     let iter = unsafe { &mut *(iter as *mut ResultsIterator<M::V>) };
199     if iter.pos >= iter.results.len() {
200         null_mut()
201     } else {
202         let res = iter.results[iter.pos] as *const M::V as *const c_void;
203         iter.pos += 1;
204         res
205     }
206 }
207 
json_api_len<M: Manager>(_: M, iter: *const c_void) -> size_t208 pub fn json_api_len<M: Manager>(_: M, iter: *const c_void) -> size_t {
209     let iter = unsafe { &*(iter as *mut ResultsIterator<M::V>) };
210     iter.results.len() as size_t
211 }
212 
json_api_free_iter<M: Manager>(_: M, iter: *mut c_void)213 pub fn json_api_free_iter<M: Manager>(_: M, iter: *mut c_void) {
214     unsafe {
215         Box::from_raw(iter as *mut ResultsIterator<M::V>);
216     }
217 }
218 
219 #[allow(clippy::not_unsafe_ptr_arg_deref)]
json_api_get<M: Manager>(_: M, val: *const c_void, path: *const c_char) -> *const c_void220 pub fn json_api_get<M: Manager>(_: M, val: *const c_void, path: *const c_char) -> *const c_void {
221     let v = unsafe { &*(val as *const M::V) };
222     let mut selector = Selector::new();
223     selector.value(v);
224     let path = unsafe { CStr::from_ptr(path).to_str().unwrap() };
225     if selector.str_path(path).is_err() {
226         return null();
227     }
228     match selector.select() {
229         Ok(s) => Box::into_raw(Box::new(ResultsIterator { results: s, pos: 0 })) as *mut c_void,
230         Err(_) => null(),
231     }
232 }
233 
json_api_is_json<M: Manager>(m: M, key: *mut rawmod::RedisModuleKey) -> c_int234 pub fn json_api_is_json<M: Manager>(m: M, key: *mut rawmod::RedisModuleKey) -> c_int {
235     match m.is_json(key) {
236         Ok(res) => res as c_int,
237         Err(_) => 0,
238     }
239 }
240 
get_llapi_ctx() -> Context241 pub fn get_llapi_ctx() -> Context {
242     Context::new(unsafe { LLAPI_CTX.unwrap() })
243 }
244 
245 #[macro_export]
246 macro_rules! redis_json_module_export_shared_api {
247     (
248         get_manage: $get_manager_expr:expr,
249         pre_command_function: $pre_command_function_expr:expr,
250     ) => {
251         #[no_mangle]
252         pub extern "C" fn JSONAPI_openKey(
253             ctx: *mut rawmod::RedisModuleCtx,
254             key_str: *mut rawmod::RedisModuleString,
255         ) -> *mut c_void {
256             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
257 
258             let m = $get_manager_expr;
259             match m {
260                 Some(mngr) => json_api_open_key_internal(mngr, ctx, RedisString::new(ctx, key_str))
261                     as *mut c_void,
262                 None => json_api_open_key_internal(
263                     manager::RedisJsonKeyManager {
264                         phantom: PhantomData,
265                     },
266                     ctx,
267                     RedisString::new(ctx, key_str),
268                 ) as *mut c_void,
269             }
270         }
271 
272         #[no_mangle]
273         #[allow(clippy::not_unsafe_ptr_arg_deref)]
274         pub extern "C" fn JSONAPI_openKeyFromStr(
275             ctx: *mut rawmod::RedisModuleCtx,
276             path: *const c_char,
277         ) -> *mut c_void {
278             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
279 
280             let key = unsafe { CStr::from_ptr(path).to_str().unwrap() };
281             let m = $get_manager_expr;
282             match m {
283                 Some(mngr) => json_api_open_key_internal(mngr, ctx, RedisString::create(ctx, key))
284                     as *mut c_void,
285                 None => json_api_open_key_internal(
286                     manager::RedisJsonKeyManager {
287                         phantom: PhantomData,
288                     },
289                     ctx,
290                     RedisString::create(ctx, key),
291                 ) as *mut c_void,
292             }
293         }
294 
295         #[no_mangle]
296         pub extern "C" fn JSONAPI_get(key: *const c_void, path: *const c_char) -> *const c_void {
297             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
298 
299             let m = $get_manager_expr;
300             match m {
301                 Some(mngr) => json_api_get(mngr, key, path),
302                 None => json_api_get(
303                     manager::RedisJsonKeyManager {
304                         phantom: PhantomData,
305                     },
306                     key,
307                     path,
308                 ),
309             }
310         }
311 
312         #[no_mangle]
313         pub extern "C" fn JSONAPI_next(iter: *mut c_void) -> *const c_void {
314             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
315 
316             let m = $get_manager_expr;
317             match m {
318                 Some(mngr) => json_api_next(mngr, iter),
319                 None => json_api_next(
320                     manager::RedisJsonKeyManager {
321                         phantom: PhantomData,
322                     },
323                     iter,
324                 ),
325             }
326         }
327 
328         #[no_mangle]
329         pub extern "C" fn JSONAPI_len(iter: *const c_void) -> size_t {
330             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
331 
332             let m = $get_manager_expr;
333             match m {
334                 Some(mngr) => json_api_len(mngr, iter),
335                 None => json_api_len(
336                     manager::RedisJsonKeyManager {
337                         phantom: PhantomData,
338                     },
339                     iter,
340                 ),
341             }
342         }
343 
344         #[no_mangle]
345         pub extern "C" fn JSONAPI_freeIter(iter: *mut c_void) {
346             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
347 
348             let m = $get_manager_expr;
349             match m {
350                 Some(mngr) => json_api_free_iter(mngr, iter),
351                 None => json_api_free_iter(
352                     manager::RedisJsonKeyManager {
353                         phantom: PhantomData,
354                     },
355                     iter,
356                 ),
357             }
358         }
359 
360         #[no_mangle]
361         pub extern "C" fn JSONAPI_getAt(json: *const c_void, index: size_t) -> *const c_void {
362             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
363 
364             let m = $get_manager_expr;
365             match m {
366                 Some(mngr) => json_api_get_at(mngr, json, index),
367                 None => json_api_get_at(
368                     manager::RedisJsonKeyManager {
369                         phantom: PhantomData,
370                     },
371                     json,
372                     index,
373                 ),
374             }
375         }
376 
377         #[no_mangle]
378         pub extern "C" fn JSONAPI_getLen(json: *const c_void, count: *mut size_t) -> c_int {
379             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
380 
381             let m = $get_manager_expr;
382             match m {
383                 Some(mngr) => json_api_get_len(mngr, json, count),
384                 None => json_api_get_len(
385                     manager::RedisJsonKeyManager {
386                         phantom: PhantomData,
387                     },
388                     json,
389                     count,
390                 ),
391             }
392         }
393 
394         #[no_mangle]
395         pub extern "C" fn JSONAPI_getType(json: *const c_void) -> c_int {
396             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
397 
398             let m = $get_manager_expr;
399             match m {
400                 Some(mngr) => json_api_get_type(mngr, json),
401                 None => json_api_get_type(
402                     manager::RedisJsonKeyManager {
403                         phantom: PhantomData,
404                     },
405                     json,
406                 ),
407             }
408         }
409 
410         #[no_mangle]
411         pub extern "C" fn JSONAPI_getInt(json: *const c_void, val: *mut c_longlong) -> c_int {
412             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
413 
414             let m = $get_manager_expr;
415             match m {
416                 Some(mngr) => json_api_get_int(mngr, json, val),
417                 None => json_api_get_int(
418                     manager::RedisJsonKeyManager {
419                         phantom: PhantomData,
420                     },
421                     json,
422                     val,
423                 ),
424             }
425         }
426 
427         #[no_mangle]
428         pub extern "C" fn JSONAPI_getDouble(json: *const c_void, val: *mut c_double) -> c_int {
429             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
430 
431             let m = $get_manager_expr;
432             match m {
433                 Some(mngr) => json_api_get_double(mngr, json, val),
434                 None => json_api_get_double(
435                     manager::RedisJsonKeyManager {
436                         phantom: PhantomData,
437                     },
438                     json,
439                     val,
440                 ),
441             }
442         }
443 
444         #[no_mangle]
445         pub extern "C" fn JSONAPI_getBoolean(json: *const c_void, val: *mut c_int) -> c_int {
446             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
447 
448             let m = $get_manager_expr;
449             match m {
450                 Some(mngr) => json_api_get_boolean(mngr, json, val),
451                 None => json_api_get_boolean(
452                     manager::RedisJsonKeyManager {
453                         phantom: PhantomData,
454                     },
455                     json,
456                     val,
457                 ),
458             }
459         }
460 
461         #[no_mangle]
462         pub extern "C" fn JSONAPI_getString(
463             json: *const c_void,
464             str: *mut *const c_char,
465             len: *mut size_t,
466         ) -> c_int {
467             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
468 
469             let m = $get_manager_expr;
470             match m {
471                 Some(mngr) => json_api_get_string(mngr, json, str, len),
472                 None => json_api_get_string(
473                     manager::RedisJsonKeyManager {
474                         phantom: PhantomData,
475                     },
476                     json,
477                     str,
478                     len,
479                 ),
480             }
481         }
482 
483         #[no_mangle]
484         pub extern "C" fn JSONAPI_getJSON(
485             json: *const c_void,
486             ctx: *mut rawmod::RedisModuleCtx,
487             str: *mut *mut rawmod::RedisModuleString,
488         ) -> c_int {
489             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
490 
491             let m = $get_manager_expr;
492             match m {
493                 Some(mngr) => json_api_get_json(mngr, json, ctx, str),
494                 None => json_api_get_json(
495                     manager::RedisJsonKeyManager {
496                         phantom: PhantomData,
497                     },
498                     json,
499                     ctx,
500                     str,
501                 ),
502             }
503         }
504 
505         #[no_mangle]
506         pub extern "C" fn JSONAPI_isJSON(key: *mut rawmod::RedisModuleKey) -> c_int {
507             $pre_command_function_expr(&get_llapi_ctx(), &Vec::new());
508 
509             let m = $get_manager_expr;
510             match m {
511                 Some(mngr) => json_api_is_json(mngr, key),
512                 None => json_api_is_json(
513                     manager::RedisJsonKeyManager {
514                         phantom: PhantomData,
515                     },
516                     key,
517                 ),
518             }
519         }
520 
521         static REDISJSON_GETAPI: &str = concat!("RedisJSON_V1", "\0");
522 
523         pub fn export_shared_api(ctx: &Context) {
524             ctx.log_notice("Exported RedisJSON_V1 API");
525             unsafe {
526                 LLAPI_CTX = Some(rawmod::RedisModule_GetThreadSafeContext.unwrap()(
527                     std::ptr::null_mut(),
528                 ))
529             };
530             ctx.export_shared_api(
531                 &JSONAPI as *const RedisJSONAPI_V1 as *const c_void,
532                 REDISJSON_GETAPI.as_ptr() as *const c_char,
533             );
534         }
535 
536         static JSONAPI: RedisJSONAPI_V1 = RedisJSONAPI_V1 {
537             openKey: JSONAPI_openKey,
538             openKeyFromStr: JSONAPI_openKeyFromStr,
539             get: JSONAPI_get,
540             next: JSONAPI_next,
541             len: JSONAPI_len,
542             freeIter: JSONAPI_freeIter,
543             getAt: JSONAPI_getAt,
544             getLen: JSONAPI_getLen,
545             getType: JSONAPI_getType,
546             getInt: JSONAPI_getInt,
547             getDouble: JSONAPI_getDouble,
548             getBoolean: JSONAPI_getBoolean,
549             getString: JSONAPI_getString,
550             getJSON: JSONAPI_getJSON,
551             isJSON: JSONAPI_isJSON,
552         };
553 
554         #[repr(C)]
555         #[derive(Copy, Clone)]
556         #[allow(non_snake_case)]
557         pub struct RedisJSONAPI_V1 {
558             pub openKey: extern "C" fn(
559                 ctx: *mut rawmod::RedisModuleCtx,
560                 key_str: *mut rawmod::RedisModuleString,
561             ) -> *mut c_void,
562             pub openKeyFromStr:
563                 extern "C" fn(ctx: *mut rawmod::RedisModuleCtx, path: *const c_char) -> *mut c_void,
564             pub get: extern "C" fn(val: *const c_void, path: *const c_char) -> *const c_void,
565             pub next: extern "C" fn(iter: *mut c_void) -> *const c_void,
566             pub len: extern "C" fn(iter: *const c_void) -> size_t,
567             pub freeIter: extern "C" fn(iter: *mut c_void),
568             pub getAt: extern "C" fn(json: *const c_void, index: size_t) -> *const c_void,
569             pub getLen: extern "C" fn(json: *const c_void, len: *mut size_t) -> c_int,
570             pub getType: extern "C" fn(json: *const c_void) -> c_int,
571             pub getInt: extern "C" fn(json: *const c_void, val: *mut c_longlong) -> c_int,
572             pub getDouble: extern "C" fn(json: *const c_void, val: *mut c_double) -> c_int,
573             pub getBoolean: extern "C" fn(json: *const c_void, val: *mut c_int) -> c_int,
574             pub getString: extern "C" fn(
575                 json: *const c_void,
576                 str: *mut *const c_char,
577                 len: *mut size_t,
578             ) -> c_int,
579             pub getJSON: extern "C" fn(
580                 json: *const c_void,
581                 ctx: *mut rawmod::RedisModuleCtx,
582                 str: *mut *mut rawmod::RedisModuleString,
583             ) -> c_int,
584             pub isJSON: extern "C" fn(key: *mut rawmod::RedisModuleKey) -> c_int,
585         }
586     };
587 }
588