| '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 |
| }; |