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
use redis_module::{Context, RedisError, RedisResult, redis_module, redis_command, RedisValue};
use regex::Regex;

const REDIS_COMMAND_KEYS: &'static str = "KEYS";

const REDIS_COMMAND_MGET: &'static str = "MGET";

fn get_regular_expression(raw_expression: &str) -> Result<Regex, RedisError> {
    match Regex::new(&raw_expression) {
        Ok(reg) => Ok(reg),
        Err(e) => Err(RedisError::String(format!("{}", e)))
    }
}

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<String>) -> RedisResult {
    let need_arg: Vec<String> = args.into_iter().skip(1).map(|s| s).collect();

    let first_arg: &str = match need_arg.get(0) {
        Some(data) => Ok(data),
        None => {
            ctx.log_debug("Not found regular expression arguments value");
            Err(RedisError::WrongArity)
        }
    }?;

    let reg = get_regular_expression(first_arg)?;

    let response_from_command = ctx.call(REDIS_COMMAND_KEYS, &["*"])?;

    let result = match response_from_command {
        RedisValue::Array(data) => Ok(data),
        _ => Err(RedisError::Str("Wrong return result from `KEYS` command, expected array.")),
    }?;

    let result_redis_value: Vec<RedisValue> = handle_redis_command_result(result)
        .filter(move |s| reg.is_match(&s))
        .map(|s| RedisValue::SimpleString(s))
        .collect();

    if result_redis_value.is_empty() {
        Ok(RedisValue::None)
    } else {
        Ok(RedisValue::Array(result_redis_value))
    }
}

fn find_values_by_rg(ctx: &Context, args: Vec<String>) -> RedisResult {
    let args: Vec<String> = args.into_iter().skip(1).collect();

    if args.len() < 2 {
        return Err(RedisError::WrongArity);
    }

    let key = match args.get(0) {
        Some(key) => Ok(key),
        None => Err(RedisError::Str("First argument must data key")),
    }?;

    let raw_reg = match args.get(1) {
        Some(reg) => Ok(reg),
        None => Err(RedisError::Str("Second argument must regular expression")),
    }?;

    let reg = get_regular_expression(raw_reg)?;

    let response_from_keys_command = ctx.call(REDIS_COMMAND_KEYS, &[key])?;

    let result_keys_command = match response_from_keys_command {
        RedisValue::Array(data) => Ok(data),
        _ => Err(RedisError::Str("Wrong return result from `MGET` command, expected array.")),
    }?;

    let result_redis_keys: Vec<String> = handle_redis_command_result(result_keys_command)
        .collect();

    let response_from_mget_command = ctx.call(
        REDIS_COMMAND_MGET,
        &result_redis_keys.iter().map(|k| k.as_ref()).collect::<Vec<&str>>()
    )?;

    let result_from_mget_command = match response_from_mget_command {
        RedisValue::Array(data) => Ok(data),
        _ => Err(RedisError::Str("Wrong return result from `KEYS` command, expected array.")),
    }?;

    let result_redis_value: Vec<RedisValue> = handle_redis_command_result(result_from_mget_command)
        .filter(move |s| reg.is_match(&s))
        .map(|s| RedisValue::SimpleString(s))
        .collect();

    if result_redis_value.is_empty() {
        Ok(RedisValue::None)
    } else {
        Ok(RedisValue::Array(result_redis_value))
    }
}

redis_module!{
    name: "Regex",
    version: 0.1,
    data_types: [],
    commands: [
        ["rgkeys", find_keys_by_rg, "readonly"],
        ["rgvalues", find_values_by_rg, "readonly"],
    ]
}