const EngineScript = require("./enginescript");
const fs = require("fs");
const path = require("path")
const toolbox = require("tinytoolbox")
const manifest = {
name: 'org.js.diddle.engine.store',
version: '0.1'
};
/**
* @property {string} filename The filename for the object currently accessing
* @property {string} directory Directory where the file exists
* @property {string} location Absolute file location
* @property {bool} hasUpdated If true there are pending changes to sync.
*/
class StorageObject {
hasUpdated = false;
_data = {
meta: {
name: '',
id: '',
},
content: null
}
/**
* @param {string} _filename A Valid JSON Filename
* @param {string} _directory A Valid Directory
*/
constructor(_filename,_directory) {
this.filename = _filename;
this.directory = _directory;
this.location = path.join(_directory,_filename);
if (!fs.existsSync(_directory)) {
fs.mkdirSync(_directory,{recursive:true});
}
this.sync()
if (this._data.meta.id.length < 1) {
this.resetID();
}
this.sync()
}
/**
* @description Sync data from the StorageObject to the File if there are changes or sync the File to this StorageObject if there are no pending changes.
*/
sync() {
try {
if (!fs.existsSync(this.location)) {
fs.writeFileSync(this.location,JSON.stringify(this._data,null,'\t'));
}
if (this.hasUpdated) {
fs.writeFileSync(this.location,JSON.stringify(this._data,null,'\t'))
} else {
this._data = require(this.location);
}
this.hasUpdated = false;
} catch(error) {
console.error(`\x1b[41m====== [FATAL STORAGE ERROR] ======\x1b[0m\n${this.location}\n`,error,`\n\x1b[41m====== ##################### ======\x1b[0m`)
return error;
}
}
/**
* @description Reset the storage objects Unique Identifier
*/
resetID() {
this._data.meta.id = toolbox.stringGen(16,3);
this.hasUpdated = true;
}
/**
*
* @returns {*} Data stored in <code>_data.meta.content</code>
*/
get() {
return this._data.content;
}
/**
*
* @param {*} data Set this StorageObjects content
* @returns {*} <code>_data.meta.content</code>
*/
set(data) {
this.hasUpdated = true;
this._data.content = data;
return this._data.content;
}
}
class StorageManager extends EngineScript {
/**
*
* @param {DiddleEngine} diddle
*/
constructor(diddle) {
super(diddle,manifest);
}
_ready() {
this.directory = path.resolve(this.diddle.pacman.get("org.js.diddle.engine.config").get().data);
if (!fs.existsSync(this.directory)) {
try {
fs.mkdirSync(this.directory);
} catch (e) {
this.log.error(`failed to create data directory;\n${e.stack}`);
process.exit(1);
}
this.log.debug(`created data directory`);
}
var files = fs.readdirSync(this.directory);
for (let i = 0; i < files.length; i++) {
var fname = files[i];
switch (fname.startsWith) {
case "js":
case "json":
this._cache.push(new StorageObject(fname,this.directory));
break;
}
}
}
/** @type {StorageObject[]} */
_cache = []
/**
* @description Fetch storage object with matching ID
* @param {string} StorageID Get storage object with the matching Storage ID. When undefined <code>StorageObject._cache</code> is returned
* @returns {StorageObject}
* @returns {StorageObject[]} Only when no parameters are passed
*/
get(StorageID) {
if (StorageID == undefined) return this._cache;
var res = this._cache.filter(s => s._data.meta.id == StorageID);
return res[0]
}
/**
* @description Returns all storage objects with the matching meta name
* @param {string} StorageName
* @returns {StorageObject[]}
*/
getByName(StorageName) {
return this._cache.filter(s => s._data.meta.name == StorageName);
}
/**
* @description Fetch all Storage Objects matching the filename.
* @param {string} fname Filename
* @returns {StorageObject[]}
*/
getByFilename(fname) {
return this._cache.filter(s => s.filename == fname);
}
create(_name) {
let so = new StorageObject(`${_name}.json`,this.directory);
this._cache.push(so);
return so;
}
}
module.exports = StorageManager;