1 | var helpers = require("./helpers.js");
|
2 | var Item = require("./item.js");
|
3 |
|
4 |
|
5 | var mockCallback = helpers.mockCallback;
|
6 |
|
7 | var validKeyType = function(mockInstance, key, callback) {
|
8 | return helpers.validKeyType(mockInstance, key, 'list', callback)
|
9 | };
|
10 |
|
11 | var initKey = function(mockInstance, key) {
|
12 | return helpers.initKey(mockInstance, key, Item.createList);
|
13 | };
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | exports.llen = function (mockInstance, key, callback) {
|
19 | var length = mockInstance.storage[key] ? mockInstance.storage[key].value.length : 0;
|
20 | mockInstance._callCallback(callback, null, length);
|
21 | };
|
22 |
|
23 | var push = function (fn, args) {
|
24 | var len = args.length;
|
25 | if (len < 2) {
|
26 | return
|
27 | }
|
28 | var mockInstance = args[0];
|
29 | var key = args[1];
|
30 | var callback = helpers.parseCallback(args);
|
31 | if (callback == undefined) {
|
32 | callback = mockCallback;
|
33 | }
|
34 | if (!validKeyType(mockInstance, key, callback)) {
|
35 | return
|
36 | }
|
37 |
|
38 | initKey(mockInstance, key);
|
39 |
|
40 |
|
41 | var values = [];
|
42 | for (var i=2, val; i < len; i++) {
|
43 | val = args[i];
|
44 | if ('function' == typeof val) {
|
45 | break;
|
46 | }
|
47 | values.push(val);
|
48 | }
|
49 | fn.call(mockInstance.storage[key], values);
|
50 | var length = mockInstance.storage[key].value.length;
|
51 | pushListWatcher.pushed(key);
|
52 | mockInstance._callCallback(callback, null, length);
|
53 | };
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | exports.lpush = function () {
|
59 | push(Item._list.prototype.lpush, arguments);
|
60 | };
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | exports.rpush = function () {
|
66 | push(Item._list.prototype.rpush, arguments);
|
67 | };
|
68 |
|
69 | var pushx = function (fn, mockInstance, key, value, callback) {
|
70 | var length = 0;
|
71 | if (mockInstance.storage[key]) {
|
72 | if (mockInstance.storage[key].type !== "list") {
|
73 | return mockInstance._callCallback(callback,
|
74 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
75 | }
|
76 | fn.call(mockInstance.storage[key], [value]);
|
77 | length = mockInstance.storage[key].value.length;
|
78 | pushListWatcher.pushed(key);
|
79 | }
|
80 | mockInstance._callCallback(callback, null, length);
|
81 | };
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | exports.rpushx = function (mockInstance, key, value, callback) {
|
87 | pushx(Item._list.prototype.rpush, mockInstance, key, value, callback);
|
88 | };
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | exports.lpushx = function (mockInstance, key, value, callback) {
|
94 | pushx(Item._list.prototype.lpush, mockInstance, key, value, callback);
|
95 | };
|
96 |
|
97 | var pop = function (fn, mockInstance, key, callback) {
|
98 | var val = null;
|
99 | if (mockInstance.storage[key] && mockInstance.storage[key].type !== "list") {
|
100 | return mockInstance._callCallback(callback,
|
101 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
102 | }
|
103 | if (mockInstance.storage[key] && mockInstance.storage[key].value.length > 0) {
|
104 | val = fn.call(mockInstance.storage[key]);
|
105 | }
|
106 | mockInstance._callCallback(callback, null, val);
|
107 | };
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | exports.lpop = function (mockInstance, key, callback) {
|
113 | pop.call(this, Item._list.prototype.lpop, mockInstance, key, callback);
|
114 | };
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | exports.rpop = function (mockInstance, key, callback) {
|
120 | pop.call(this, Item._list.prototype.rpop, mockInstance, key, callback);
|
121 | };
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | exports.rpoplpush = function(mockInstance, sourceKey, destinationKey, callback) {
|
127 | pop.call(this, Item._list.prototype.rpop, mockInstance, sourceKey, function (err, reply) {
|
128 | if (err) {
|
129 | return mockInstance._callCallback(callback, err, null);
|
130 | }
|
131 | if (reply === null || reply === undefined) {
|
132 | return mockInstance._callCallback(callback, null);
|
133 | }
|
134 | push(Item._list.prototype.lpush, [
|
135 | mockInstance,
|
136 | destinationKey,
|
137 | reply,
|
138 | function (err) {
|
139 | if (err) {
|
140 | return mockInstance._callCallback(callback, err, null);
|
141 | }
|
142 | return mockInstance._callCallback(callback, null, reply);
|
143 | }
|
144 | ]);
|
145 | });
|
146 | };
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | var listenToPushOnLists = function (mockInstance, keys, timeout, callback) {
|
152 | var listenedTo = [];
|
153 | var expire = null;
|
154 | var listener = function (key) {
|
155 |
|
156 | pushListWatcher.removeListeners(listenedTo, listener);
|
157 | if (expire) {
|
158 | clearTimeout(expire);
|
159 | }
|
160 | callback(key);
|
161 | };
|
162 |
|
163 | for (var i = 0; i < keys.length; i++) {
|
164 | listenedTo.push(keys[i]);
|
165 | pushListWatcher.suscribe(keys[i], listener);
|
166 | }
|
167 | if (timeout > 0) {
|
168 | expire = setTimeout(function () {
|
169 | pushListWatcher.removeListeners(listenedTo, listener);
|
170 | callback(null);
|
171 | }, timeout * 1000);
|
172 | if (expire.unref) {
|
173 | expire.unref();
|
174 | }
|
175 | }
|
176 | };
|
177 |
|
178 |
|
179 |
|
180 |
|
181 | var bpop = function (fn, mockInstance, keys, timeout, callback) {
|
182 | var val = null;
|
183 |
|
184 | for (var i = 0; i < keys.length; i++) {
|
185 | if (mockInstance.storage[keys[i]] && mockInstance.storage[keys[i]].value.length > 0) {
|
186 | var key = keys[i];
|
187 | val = fn.call(mockInstance.storage[key]);
|
188 | mockInstance._callCallback(callback, null, [key, val]);
|
189 | return;
|
190 | }
|
191 | }
|
192 |
|
193 | listenToPushOnLists(mockInstance, keys, timeout, function (key) {
|
194 | if (key !== null) {
|
195 | val = fn.call(mockInstance.storage[key]);
|
196 | mockInstance._callCallback(callback, null, [key, val]);
|
197 | } else {
|
198 | mockInstance._callCallback(callback, null, null);
|
199 | }
|
200 |
|
201 | });
|
202 | };
|
203 |
|
204 |
|
205 |
|
206 |
|
207 | exports.blpop = function (mockInstance, keys, timeout, callback) {
|
208 | bpop.call(this, Item._list.prototype.lpop, mockInstance, keys, timeout, callback);
|
209 | };
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | exports.brpop = function (mockInstance, keys, timeout, callback) {
|
215 | bpop.call(this, Item._list.prototype.rpop, mockInstance, keys, timeout, callback);
|
216 | };
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | exports.lindex = function (mockInstance, key, index, callback) {
|
222 | var val = null;
|
223 | if (mockInstance.storage[key]) {
|
224 | if (mockInstance.storage[key].type !== "list") {
|
225 | return mockInstance._callCallback(callback,
|
226 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
227 | }
|
228 |
|
229 | if (index < 0 && -mockInstance.storage[key].value.length <= index) {
|
230 | val = mockInstance.storage[key].value[mockInstance.storage[key].value.length + index];
|
231 | } else if (mockInstance.storage[key].value.length > index) {
|
232 | val = mockInstance.storage[key].value[index];
|
233 | }
|
234 | }
|
235 | mockInstance._callCallback(callback, null, val);
|
236 | };
|
237 |
|
238 |
|
239 |
|
240 |
|
241 | exports.lrange = function (mockInstance, key, startIndex, stopIndex, callback) {
|
242 | var val = [];
|
243 | var index1 = startIndex;
|
244 | var index2 = stopIndex;
|
245 |
|
246 | if (mockInstance.storage[key]) {
|
247 | if (mockInstance.storage[key].type !== "list") {
|
248 | return mockInstance._callCallback(callback,
|
249 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
250 | }
|
251 |
|
252 | index1 = index1 >= 0 ? index1 : Math.max(mockInstance.storage[key].value.length + index1, 0);
|
253 | index2 = index2 >= 0 ? index2 : Math.max(mockInstance.storage[key].value.length + index2, 0);
|
254 | val = mockInstance.storage[key].value.slice(index1, index2 + 1);
|
255 | }
|
256 | mockInstance._callCallback(callback, null, val);
|
257 | };
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | exports.lrem = function (mockInstance, key, count, value, callback) {
|
263 | var removedCount = 0;
|
264 |
|
265 | if (mockInstance.storage[key]) {
|
266 | if (mockInstance.storage[key].type !== "list") {
|
267 | return mockInstance._callCallback(callback,
|
268 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
269 | }
|
270 |
|
271 | var list = mockInstance.storage[key].value;
|
272 |
|
273 | var strValue = Item._stringify(value);
|
274 |
|
275 | var filteredList = [];
|
276 | if (count > 0) {
|
277 |
|
278 | for (var i = 0; i < list.length; ++i) {
|
279 | if (list[i] == strValue && count > 0) {
|
280 | --count;
|
281 | ++removedCount;
|
282 | } else {
|
283 | filteredList.push(list[i]);
|
284 | }
|
285 | }
|
286 | } else if (count < 0) {
|
287 |
|
288 | for (i = list.length; i > 0; --i) {
|
289 | if (list[i-1] == strValue && count < 0) {
|
290 | ++count;
|
291 | ++removedCount;
|
292 | } else {
|
293 | filteredList.unshift(list[i-1]);
|
294 | }
|
295 | }
|
296 | } else {
|
297 |
|
298 | for (i = 0; i < list.length; ++i) {
|
299 | if (list[i] === strValue) {
|
300 | ++removedCount;
|
301 | } else {
|
302 | filteredList.push(list[i]);
|
303 | }
|
304 | }
|
305 | }
|
306 |
|
307 | mockInstance.storage[key].value = filteredList;
|
308 | }
|
309 | mockInstance._callCallback(callback, null, removedCount);
|
310 | };
|
311 |
|
312 |
|
313 |
|
314 |
|
315 | exports.lset = function (mockInstance, key, index, value, callback) {
|
316 | var res = "OK";
|
317 | var len = -1;
|
318 | if (!mockInstance.storage[key]) {
|
319 | return mockInstance._callCallback(callback,
|
320 | new Error("ERR no such key"));
|
321 | }
|
322 | if (mockInstance.storage[key].type !== "list") {
|
323 | return mockInstance._callCallback(callback,
|
324 | new Error("ERR Operation against a key holding the wrong kind of value"));
|
325 | }
|
326 | len = mockInstance.storage[key].value.length;
|
327 | if (len <= index || -len > index) {
|
328 | return mockInstance._callCallback(callback,
|
329 | new Error("ERR index out of range"));
|
330 | }
|
331 | if (index < 0) {
|
332 | mockInstance.storage[key].value[len + index] = Item._stringify(value);
|
333 | } else {
|
334 | mockInstance.storage[key].value[index] = Item._stringify(value);
|
335 | }
|
336 | mockInstance._callCallback(callback, null, res);
|
337 | };
|
338 |
|
339 |
|
340 |
|
341 |
|
342 | exports.ltrim = function(mockInstance, key, start, end, callback) {
|
343 | var res = "OK";
|
344 | var len = -1;
|
345 | if (!mockInstance.storage[key]) {
|
346 | return mockInstance._callCallback(callback, null, res);
|
347 | }
|
348 |
|
349 | if (mockInstance.storage[key].type !== "list") {
|
350 | return mockInstance._callCallback(callback,
|
351 | new Error("WRONGTYPE Operation against a key holding the wrong kind of value"));
|
352 | }
|
353 |
|
354 | len = mockInstance.storage[key].value.length;
|
355 |
|
356 | if (start < 0) {
|
357 | start = len + start;
|
358 | }
|
359 | if (end < 0) {
|
360 | end = len + end;
|
361 | }
|
362 | if (end >= len) {
|
363 | end = len - 1;
|
364 | }
|
365 | if (start >= len || start > end) {
|
366 |
|
367 | delete mockInstance.storage[key];
|
368 | } else {
|
369 | mockInstance.storage[key].value = mockInstance.storage[key].value.slice(start, end + 1);
|
370 | }
|
371 | mockInstance._callCallback(callback, null, res);
|
372 | };
|
373 |
|
374 |
|
375 |
|
376 |
|
377 | var PushListWatcher = function () {
|
378 | this.listeners = {};
|
379 | };
|
380 |
|
381 |
|
382 |
|
383 |
|
384 | PushListWatcher.prototype.suscribe = function (key, listener) {
|
385 | if (this.listeners[key]) {
|
386 | this.listeners[key].push(listener);
|
387 | } else {
|
388 | this.listeners[key] = [listener];
|
389 | }
|
390 | };
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 | PushListWatcher.prototype.pushed = function (key) {
|
397 | if (this.listeners[key] && this.listeners[key].length > 0) {
|
398 | var listener = this.listeners[key].shift();
|
399 | listener(key);
|
400 | }
|
401 | };
|
402 |
|
403 |
|
404 |
|
405 |
|
406 | PushListWatcher.prototype.removeListeners = function (listenedTo, listener) {
|
407 | for (var i = 0; i < listenedTo.length; i++) {
|
408 | for (var j = 0; j < this.listeners[listenedTo[i]].length; j++) {
|
409 | if (this.listeners[listenedTo[i]][j] === listener) {
|
410 | this.listeners[listenedTo[i]].splice(j, 1);
|
411 | j = this.listeners[listenedTo[i]];
|
412 | }
|
413 | }
|
414 | }
|
415 | };
|
416 |
|
417 | var pushListWatcher = new PushListWatcher();
|