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