From f000cd06d7400599eea8062de860a8390da9ab73 Mon Sep 17 00:00:00 2001 From: Michal Szczepanski Date: Tue, 18 Aug 2020 20:26:21 +0200 Subject: [PATCH] Add initial structure with queue implementation --- .gitignore | 5 + LICENSE | 26 ++++ example/structures.js | 19 +++ package.json | 28 +++++ redistructures.js | 272 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 example/structures.js create mode 100644 package.json create mode 100644 redistructures.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8afba2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# ide +*.iml +# js +node_modules +package-lock.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..275de44 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright 2020, Michal Szczepanski + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/example/structures.js b/example/structures.js new file mode 100644 index 0000000..2508937 --- /dev/null +++ b/example/structures.js @@ -0,0 +1,19 @@ +const { Struct } = require('../redistructures') +const q = Struct.queue(); +q.add('x'); +q.add('y'); +q.add('z'); +const queueHandler = (res) => { + if (res) { + console.log(res); + getQueue(queueHandler); + } else { + console.log('finished'); + process.exit(0); + } +} +const getQueue = (success, error) => { + return q.get(1).then(success, error); +} +getQueue(queueHandler); + diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d3bc2d --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "redistructures-node", + "version": "0.0.1", + "description": "redis structures in node", + "main": "redistructures.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vane/redistructures-node.git" + }, + "keywords": [ + "redis", + "data-structures", + "node", + "javascript" + ], + "author": "Michal Szczepanski", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/vane/redistructures-node/issues" + }, + "homepage": "https://github.com/vane/redistructures-node#readme", + "dependencies": { + "redis": "^3.0.2" + } +} diff --git a/redistructures.js b/redistructures.js new file mode 100644 index 0000000..7d3c129 --- /dev/null +++ b/redistructures.js @@ -0,0 +1,272 @@ +const redis = require("redis"); + +class Connection { + REDIS = null; + HOST = 'localhost'; + PORT = 6379; + DB = 0; + static initConnection(host='localhost', port=6379, db=0) { + Connection.HOST = host; + Connection.PORT = port; + Connection.DB = db; + } + + static getConnection() { + if(!Connection.REDIS) { + const fn = (host, port, db) => { + return redis.createClient(host, port, db); + } + Connection.REDIS = fn(Connection.HOST, Connection.port, Connection.db); + } + return Connection.REDIS; + } +} + +class Struct { + + static set(key='set') { + return null; + } + + static dictionary(key='dict') { + return null; + } + + static queue(key='queue') { + return new Queue(Connection.getConnection(), key); + } + + static counter(key='counter') { + return null; + } + + static list(key='list') { + return null; + } + +} + +class Queue { + constructor(connection, key) { + this.connection = connection; + this.key = key; + } + + get(timeout=0) { + const p = new Promise((resolve, reject) => { + this.connection.brpop(this.key, timeout, (err, res) => { + if (err) { + reject(err); + } else { + if(res) { + resolve(res[1]); + } else { + resolve(res); + } + } + }); + }); + return p; + } + + add(value) { + this.connection.lpush(this.key, value); + } +} +module.exports.Struct = Struct +/* + +class Dict: + """Dictionary on top of redis""" + def __init__(self, connection, key="dict"): + self._conn = connection + self.key = key + + def exists(self, key): + if self._conn.exists(f"{self.key}:{key}"): + return True + + def __setitem__(self, key, value): + self._conn.set(f"{self.key}:{key}", value) + return value + + def __getitem__(self, key): + return self._conn.get(f"{self.key}:{key}") + + def __contains__(self, key): + return self._conn.get(f"{self.key}:{key}") + + def keys(self, wildcard="*"): + return self._conn.scan_iter(f"{self.key}:{wildcard}") + + def values(self, wildcard="*"): + iter = self._conn.scan_iter(f"{self.key}:{wildcard}") + for key in iter: + yield self._conn.get(key) + + def items(self, wildcard="*"): + iter = self._conn.scan_iter(f"{self.key}:{wildcard}") + for key in iter: + yield key, self._conn.get(key) + + def set(self, key, value): + self._conn.set(f"{self.key}:{key}", value) + return value + + def get(self, key): + return self._conn.get(f"{self.key}:{key}") + + def getcheck(self, key): + if self.exists(key): + return self.get(f"{self.key}:{key}") + return False + + +class SetIterator: + """Set iterator on top of redis""" + def __init__(self, connection, key="set"): + self._conn = connection + self._key = key + self._iter = self._conn.sscan_iter(self._key) + + def __next__(self): + """Iterator next value""" + return next(self._iter) + + def next(self): + """Iterator next value""" + return next(self._iter) + + def __iter__(self): + return self + + +class Set: + """Set on top of redis""" + def __init__(self, connection, key="set"): + self._conn = connection + self._key = key + + @property + def key(self): + return self._key + + def add(self, value): + """Add value to set""" + self._conn.sadd(self._key, value) + + def remove(self, value): + """Remove value from set""" + self._conn.srem(self._key, value) + + def pyset(self): + return self._conn.smembers(self._key) + + def __len__(self): + """Set length""" + return self._conn.scard(self._key) + + def __contains__(self, value): + """Check if value is in set""" + return self._conn.sismember(self._key, value) + + def __iter__(self): + """Return set iterator @see SetIterator""" + return SetIterator(connection=self._conn, key=self._key) + + def __add__(self, set2): + """Add two set together based on provided set key""" + return self._conn.sunion(self._key, set2.key) + + def __sub__(self, set2): + """Substracts two sets based on provided set key""" + return self._conn.sdiff(self._key, set2.key) + + def __repr__(self): + return "{}".format(self.pyset()) + + +class Counter: + def __init__(self, connection, key="counter"): + self._conn = connection + self._key = key + if self._conn.exists(key): + self._count = int(self._conn.get(key)) + else: + self._count = int(self._conn.set(key, 0)) + + @property + def key(self): + return self._key + + def value(self, padding=0): + return ("{0:0"+str(padding)+"d}").format(self._count) + + def __repr__(self): + return str(self._count) + + def incr(self, padding=0): + self._count = self._conn.incr(self._key) + return ("{0:0"+str(padding)+"d}").format(self._count) + + def decr(self, padding=0): + self._count = self._conn.decr(self._key) + return ("{0:0"+str(padding)+"d}").format(self._count) + +class ListIterator: + def __init__(self, connection, key): + self._key = key + self._conn = connection + self.index = 0 + self.end = self._conn.llen(self._key) + self.current = None + + def __next__(self): + """Iterator next value""" + self.index += 1 + val = self._conn.lindex(self._key, self.index) + if val: + return val + raise StopIteration() + + def next(self): + """Iterator next value""" + self.index += 1 + val = self._conn.lindex(self._key, self.index) + if val: + return val + raise StopIteration() + + def __iter__(self): + return self + +class List: + def __init__(self, connection, key='list'): + self._conn = connection + self._key = key + + def append(self, value): + return self._conn.rpush(self._key, value) + + def insert(self, index, value): + return slef._conn.lset(self._key, index, value) + + def pop(self, index): + return self._conn.lrem(self._key, index) + + def __getitem__(self, index): + return self._conn.lindex(self._key, index) + + def __setitem__(self, index, value): + return self._conn.lset(self._key, index, value) + + def __contains__(self, item): + return self._conn.execute_command('LPOS', self._key, item) + + def __iter__(self): + """Return set iterator @see SetIterator""" + return ListIterator(connection=self._conn, key=self._key) + + def __len__(self): + return self._conn.llen(self._key) +*/