//! {"category": "libraries"} "use strict"; class DatabaseList { constructor(name) { this._prefix = name; } async _insert(item, end, desiredKey) { let key = this._prefix + ":head"; let listNode = await database.get(key); if (!listNode) { await database.set(key, JSON.stringify({next: key, previous: key, value: item, count: 1, nextId: 1, key: desiredKey})); } if (listNode) { listNode = JSON.parse(listNode); listNode.count++; let id = this._prefix + ":node:" + desiredKey; if (desiredKey && await database.get(id)) { throw new Error("Key '" + desiredKey + "' already exists."); } if (!desiredKey) { id = this._prefix + ":node:" + listNode.nextId++; } if (end) { let newKey = id; await database.set(newKey, JSON.stringify({next: key, previous: listNode.previous, value: item})); if (listNode.previous !== key) { let previous = JSON.parse(await database.get(listNode.previous)); previous.next = newKey; await database.set(listNode.previous, JSON.stringify(previous)); listNode.previous = newKey; await database.set(key, JSON.stringify(listNode)); } else { listNode.previous = newKey; listNode.next = newKey; await database.set(key, JSON.stringify(listNode)); } } else { let newKey = listNode.key ? (this._prefix + ":node:" + listNode.key) : id; await database.set(newKey, JSON.stringify({next: listNode.next, previous: key, value: listNode.value})); listNode.value = item; listNode.key = desiredKey; if (listNode.next !== key) { let next = JSON.parse(await database.get(listNode.next)); next.previous = newKey; await database.set(listNode.next, JSON.stringify(next)); listNode.next = newKey; await database.set(key, JSON.stringify(listNode)); } else { listNode.previous = newKey; listNode.next = newKey; await database.set(key, JSON.stringify(listNode)); } } } } async _remove(end) { let key = this._prefix + ":head"; let listNode = await database.get(key); let result; if (listNode) { listNode = JSON.parse(listNode); listNode.count--; if (end) { if (listNode.previous === key) { await database.remove(key); result = listNode.value; } else { let removeKey = listNode.previous; let previous = JSON.parse(await database.get(listNode.previous)); result = previous.value; if (previous.previous !== key) { let previousPrevious = JSON.parse(await database.get(previous.previous)); previousPrevious.next = key; listNode.previous = previous.previous; await database.set(previous.previous, JSON.stringify(previousPrevious)); await database.set(key, JSON.stringify(listNode)); await database.remove(removeKey); } else { listNode.next = key; listNode.previous = key; await database.set(key, JSON.stringify(listNode)); await database.remove(removeKey); } } } else { result = listNode.value; if (listNode.next === key) { await database.remove(key); } else { let removeKey = listNode.next; let next = JSON.parse(await database.get(listNode.next)); listNode.value = next.value; if (next.next !== key) { let nextNext = JSON.parse(await database.get(next.next)); nextNext.previous = key; listNode.next = next.next; await database.set(next.next, JSON.stringify(nextNext)); await database.set(key, JSON.stringify(listNode)); await database.remove(removeKey); } else { listNode.next = key; listNode.previous = key; await database.set(key, JSON.stringify(listNode)); await database.remove(removeKey); } } } } return result; } push(item, key) { return this._insert(item, true, key); } unshift(item, key) { return this._insert(item, false, key); } pop() { return this._remove(true); } shift() { return this._remove(false); } async get(offset, count) { const head = this._prefix + ":head"; let key = head; let result = []; while (offset) { let node = await database.get(key); if (!node) { break; } node = JSON.parse(node); if (offset > 0) { key = node.next; offset--; } else if (offset < 0) { key = node.previous; offset++; } } while (count) { let node = await database.get(key); if (!node) { break; } node = JSON.parse(node); result.push(node.value); if (count > 0) { key = node.next; if (key == head) { break; } count--; } else if (count < 0) { key = node.previous; count++; if (key == head) { count = -1; } } } return result; } async getByKey(key) { let value = await database.get(this._prefix + (key ? ":node:" + key.toString() : ":head")); if (value !== undefined) { value = JSON.parse(value); } else { let node = await database.get(this._prefix + ":head"); if (node !== undefined) { node = JSON.parse(node); if (node.key == key) { value = node; } } } return value; } async setByKey(key, value) { let node = await database.get(this._prefix + ":head"); let done = false; if (node !== undefined) { node = JSON.parse(node); if (node.key == key) { node.value = value; await database.set(this._prefix + ":head", JSON.stringify(node)); done = true; } } if (!done) { node = JSON.parse(await database.get(this._prefix + ":node:" + key)); node.value = value; await database.set(this._prefix + ":node:" + key, JSON.stringify(node)); done = true; } } } function wipeDatabase() { let promises = []; return database.getAll().then(function(list) { for (let i = 0; i < list.length; i++) { promises.push(database.remove(list[i])); } }); return Promise.all(promises); } async function dumpDatabase() { for (let key of await database.getAll()) { let value = await database.get(key); try { value = JSON.parse(value); } catch (error) { // eh } terminal.print("DUMP: ", key, " ", JSON.stringify(value, 0, 2)); } } /*async function test() { let x = new DatabaseList("list"); await x.push("1"); await x.push("2"); await x.push("3"); await dumpDatabase(); terminal.print(await x.get(0, 10)); terminal.print(await x.get(-1, -10)); terminal.print(await x.pop()); terminal.print(await x.pop()); terminal.print(await x.pop()); await dumpDatabase(); await x.unshift("1"); await x.unshift("2"); await x.unshift("3"); await dumpDatabase(); await x.push("cory", "coryKey"); await x.push("yo", "yoKey"); terminal.print(await x.get(0, 10)); terminal.print(await x.shift()); terminal.print(await x.shift()); terminal.print(await x.shift()); await dumpDatabase(); }*/ if (imports.terminal) { //wipeDatabase(); //dumpDatabase().then(wipeDatabase).then(test).catch(terminal.print); /*let x = new DatabaseList("list"); core.register("onInput", function(input) { if (input == "clear") { wipeDatabase().then(function() { terminal.print("Database is now empty."); }); } else if (input.substring(0, "push ".length) == "push ") { x.push(input.substring("push ".length)).then(dumpDatabase).catch(terminal.print); } else if (input.substring(0, "unshift ".length) == "unshift ") { x.unshift(input.substring("unshift ".length)).then(dumpDatabase).catch(terminal.print); } else if (input == "pop") { x.pop().then(function(out) { terminal.print("POPPED: ", out); }).then(dumpDatabase).catch(terminal.print); } else if (input == "shift") { x.shift().then(function(out) { terminal.print("SHIFTED: ", out); }).then(dumpDatabase).catch(terminal.print); } else if (input.substring(0, "get ".length) == "get ") { let parts = input.split(" "); x.get(parseInt(parts[1])).then(function(result) { terminal.print(JSON.stringify(result)) }).catch(terminal.print); } else { dumpDatabase(); } });*/ } exports.ListStore = function(name) { let ls = new DatabaseList(name); return { push: ls.push.bind(ls), pop: ls.pop.bind(ls), shift: ls.shift.bind(ls), unshift: ls.unshift.bind(ls), get: ls.get.bind(ls), getByKey: ls.getByKey.bind(ls), setByKey: ls.setByKey.bind(ls), }; }