redis_regular_expression_module/
lib.rs1use crate::args::GetRegularExpression;
2use redis_module::{Context, RedisError, RedisResult, RedisString, RedisValue, Status, redis_command, redis_module};
3use std::convert::TryFrom;
4
5pub mod args;
6
7const REDIS_COMMAND_KEYS: &'static str = "KEYS";
8const REDIS_PATTERN_KEY_ALL: &'static str = "*";
9
10const RG_KEYS: &'static str = "rgkeys";
11const RG_VALUES: &'static str = "rgvalues";
12const RG_DELETE: &'static str = "rgdelete";
13
14const READ_ONLY: &'static str = "readonly";
15const WRITE: &'static str = "write";
16
17fn handle_redis_command_result(result: Vec<RedisValue>) -> impl Iterator<Item = String> {
18 result
19 .into_iter()
20 .map(|value| match value {
21 RedisValue::SimpleString(value) => Some(value),
22 _ => None,
23 })
24 .filter(|value| value.is_some())
25 .map(|value| value.unwrap())
26}
27
28fn find_keys_by_rg(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
29 ctx.log_debug(&format!("Run {}", RG_KEYS));
30
31 let args = crate::args::FindByKey::try_from(args)?;
32
33 let command_result = ctx.call(REDIS_COMMAND_KEYS, &[REDIS_PATTERN_KEY_ALL])?;
34
35 let command_result = match command_result {
36 RedisValue::Array(data) => Ok(data),
37 _ => Err(RedisError::Str(
38 "Wrong return result from `KEYS` command, expected array.",
39 )),
40 }?;
41
42 ctx.log_debug(&format!("{:?}", command_result));
43
44 let result: Vec<RedisValue> = handle_redis_command_result(command_result)
45 .filter(move |s| args.get_regular_expression().is_match(&s.to_string()))
46 .map(move |s| RedisValue::SimpleString(s))
47 .collect();
48
49 Ok(RedisValue::Array(result))
50}
51
52fn find_values_by_rg(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
53 ctx.log_debug(&format!("Run {}", RG_VALUES));
54
55 let args = crate::args::FindByValue::try_from(args)?;
56
57 let command_keys_result = ctx.call(
58 REDIS_COMMAND_KEYS,
59 &[args.get_redis_mask()]
60 )?;
61
62 let result_keys_command = match command_keys_result {
63 RedisValue::Array(data) => Ok(data),
64 _ => Err(RedisError::Str(
65 "Wrong return result from `MGET` command, expected array.",
66 )),
67 }?;
68
69 let result_redis_value: Vec<RedisValue> = handle_redis_command_result(result_keys_command)
70 .map(move |key| {
71 let redis_key = ctx.open_key(&ctx.create_string(&key));
72 match redis_key.read() {
73 Ok(value) => (Some(key), value),
74 Err(e) => {
75 ctx.log_debug(&format!("{}", e));
76 (None, None)
77 }
78 }
79 })
80 .filter(|(key, value)| value.is_some() && key.is_some())
81 .map(|(key, value)| (key.unwrap(), value.unwrap()))
82 .filter(move |(_, value)| args.get_regular_expression().is_match(&value))
83 .map(|(key, _)| RedisValue::SimpleString(key))
84 .collect();
85
86 Ok(RedisValue::Array(result_redis_value))
87}
88
89fn delete_keys_by_rg(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
90 ctx.log_debug(&format!("Run {}", RG_DELETE));
91
92 let args = crate::args::FindByKey::try_from(args)?;
93 let raw_regular_expression = format!("{}", args.get_regular_expression());
94
95 let command_result = ctx.call(RG_KEYS, &[&raw_regular_expression])?;
96
97 let result = match command_result {
98 RedisValue::Array(data) => Ok(data),
99 RedisValue::NoReply => {
100 return Ok(RedisValue::NoReply);
101 }
102 _ => Err(RedisError::Str(
103 "Wrong return result from `KEYS` command, expected array.",
104 )),
105 }?;
106
107 let count_delete_redis_key: u64 = handle_redis_command_result(result)
108 .map(move |key| {
109 let redis_key = ctx.open_key_writable(&ctx.create_string(&key));
110 redis_key.delete()
111 })
112 .fold(0, |acc, data| if data.is_ok() {acc + 1} else {acc});
113
114 Ok(RedisValue::Integer(count_delete_redis_key as i64))
115}
116
117redis_module! {
118 name: "RustRegxCommand",
119 version: 0.5,
120 data_types: [],
121 init: init,
122 deinit: deinit,
123 commands: [
124 [RG_KEYS, find_keys_by_rg, READ_ONLY, 1, 1, 1],
125 [RG_VALUES, find_values_by_rg, READ_ONLY, 1, 1, 1],
126 [RG_DELETE, delete_keys_by_rg, WRITE, 1, 1, 1],
127 ]
128}
129
130fn init(ctx: &Context, _args: &Vec<RedisString>) -> Status {
131 ctx.log_debug("Start");
132
133 Status::Ok
134}
135
136fn deinit(ctx: &Context) -> Status {
137 ctx.log_debug("End");
138
139 Status::Ok
140}