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