index.js
'use strict'
var commands = require('./commands.json')
/**
* Redis command list
*
* All commands are lowercased.
*
* @var {string[]}
* @public
*/
exports.list = Object.keys(commands)
var flags = {}
exports.list.forEach(function (commandName) {
flags[commandName] = commands[commandName].flags.reduce(function (flags, flag) {
flags[flag] = true
return flags
}, {})
})
/**
* Check if the command exists
*
* @param {string} commandName - the command name
* @return {boolean} result
* @public
*/
exports.exists = function (commandName) {
return Boolean(commands[commandName])
}
/**
* Check if the command has the flag
*
* Some of possible flags: readonly, noscript, loading
* @param {string} commandName - the command name
* @param {string} flag - the flag to check
* @return {boolean} result
* @public
*/
exports.hasFlag = function (commandName, flag) {
if (!flags[commandName]) {
throw new Error('Unknown command ' + commandName)
}
return Boolean(flags[commandName][flag])
}
/**
* Get indexes of keys in the command arguments
*
* @param {string} commandName - the command name
* @param {string[]} args - the arguments of the command
* @param {object} [options] - options
* @param {boolean} [options.parseExternalKey] - parse external keys
* @return {number[]} - the list of the index
* @public
*
* @example
* ```javascript
* getKeyIndexes('set', ['key', 'value']) // [0]
* getKeyIndexes('mget', ['key1', 'key2']) // [0, 1]
* ```
*/
exports.getKeyIndexes = function (commandName, args, options) {
var command = commands[commandName]
if (!command) {
throw new Error('Unknown command ' + commandName)
}
if (!Array.isArray(args)) {
throw new Error('Expect args to be an array')
}
var keys = []
var i, keyStart, keyStop, parseExternalKey
switch (commandName) {
case 'zunionstore':
case 'zinterstore':
keys.push(0)
// fall through
case 'eval':
case 'evalsha':
keyStop = Number(args[1]) + 2
for (i = 2; i < keyStop; i++) {
keys.push(i)
}
break
case 'sort':
parseExternalKey = options && options.parseExternalKey
keys.push(0)
for (i = 1; i < args.length - 1; i++) {
if (typeof args[i] !== 'string') {
continue
}
var directive = args[i].toUpperCase()
if (directive === 'GET') {
i += 1
if (args[i] !== '#') {
if (parseExternalKey) {
keys.push([i, getExternalKeyNameLength(args[i])])
} else {
keys.push(i)
}
}
} else if (directive === 'BY') {
i += 1
if (parseExternalKey) {
keys.push([i, getExternalKeyNameLength(args[i])])
} else {
keys.push(i)
}
} else if (directive === 'STORE') {
i += 1
keys.push(i)
}
}
break
case 'migrate':
if (args[2] === '') {
for (i = 5; i < args.length - 1; i++) {
if (args[i].toUpperCase() === 'KEYS') {
for (var j = i + 1; j < args.length; j++) {
keys.push(j)
}
break
}
}
} else {
keys.push(2)
}
break
case 'xreadgroup':
case 'xread':
// Keys are 1st half of the args after STREAMS argument.
for (i = commandName === 'xread' ? 0 : 3; i < args.length - 1; i++) {
if (String(args[i]).toUpperCase() === 'STREAMS') {
for (j = i + 1; j <= i + ((args.length - 1 - i) / 2); j++) {
keys.push(j)
}
break
}
}
break
default:
// Step has to be at least one in this case, otherwise the command does
// not contain a key.
if (command.step > 0) {
keyStart = command.keyStart - 1
keyStop = command.keyStop > 0 ? command.keyStop : args.length + command.keyStop + 1
for (i = keyStart; i < keyStop; i += command.step) {
keys.push(i)
}
}
break
}
return keys
}
function getExternalKeyNameLength (key) {
if (typeof key !== 'string') {
key = String(key)
}
var hashPos = key.indexOf('->')
return hashPos === -1 ? key.length : hashPos
}