blob: 52e58ecfa6c3370c6174aa8afef93f5c0451e120 [file] [log] [blame]
'use strict';
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
// These function are only called with internal data and have therefore always the same instanceof X
function replyToObject (reply) {
// The reply might be a string or a buffer if this is called in a transaction (multi)
if (reply.length === 0 || !(reply instanceof Array)) {
return null;
}
var obj = {};
for (var i = 0; i < reply.length; i += 2) {
obj[reply[i].toString('binary')] = reply[i + 1];
}
return obj;
}
function replyToStrings (reply) {
if (reply instanceof Buffer) {
return reply.toString();
}
if (reply instanceof Array) {
var res = new Array(reply.length);
for (var i = 0; i < reply.length; i++) {
// Recusivly call the function as slowlog returns deep nested replies
res[i] = replyToStrings(reply[i]);
}
return res;
}
return reply;
}
function print (err, reply) {
if (err) {
// A error always begins with Error:
console.log(err.toString());
} else {
console.log('Reply: ' + reply);
}
}
var camelCase;
// Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error)
// Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions)
// All capital letters are going to be replaced with a lower case letter and a underscore infront of it
function clone (obj) {
var copy;
if (Array.isArray(obj)) {
copy = new Array(obj.length);
for (var i = 0; i < obj.length; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
if (Object.prototype.toString.call(obj) === '[object Object]') {
copy = {};
var elems = Object.keys(obj);
var elem;
while (elem = elems.pop()) {
if (elem === 'tls') { // special handle tls
copy[elem] = obj[elem];
continue;
}
// Accept camelCase options and convert them to snake_case
var snake_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase();
// If camelCase is detected, pass it to the client, so all variables are going to be camelCased
// There are no deep nested options objects yet, but let's handle this future proof
if (snake_case !== elem.toLowerCase()) {
camelCase = true;
}
copy[snake_case] = clone(obj[elem]);
}
return copy;
}
return obj;
}
function convenienceClone (obj) {
camelCase = false;
obj = clone(obj) || {};
if (camelCase) {
obj.camel_case = true;
}
return obj;
}
function callbackOrEmit (self, callback, err, res) {
if (callback) {
callback(err, res);
} else if (err) {
self.emit('error', err);
}
}
function replyInOrder (self, callback, err, res, queue) {
// If the queue is explicitly passed, use that, otherwise fall back to the offline queue first,
// as there might be commands in both queues at the same time
var command_obj;
/* istanbul ignore if: TODO: Remove this as soon as we test Redis 3.2 on travis */
if (queue) {
command_obj = queue.peekBack();
} else {
command_obj = self.offline_queue.peekBack() || self.command_queue.peekBack();
}
if (!command_obj) {
process.nextTick(function () {
callbackOrEmit(self, callback, err, res);
});
} else {
var tmp = command_obj.callback;
command_obj.callback = tmp ?
function (e, r) {
tmp(e, r);
callbackOrEmit(self, callback, err, res);
} :
function (e, r) {
if (e) {
self.emit('error', e);
}
callbackOrEmit(self, callback, err, res);
};
}
}
module.exports = {
reply_to_strings: replyToStrings,
reply_to_object: replyToObject,
print: print,
err_code: /^([A-Z]+)\s+(.+)$/,
monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\]( ".+?")+$/,
clone: convenienceClone,
callback_or_emit: callbackOrEmit,
reply_in_order: replyInOrder
};