src/model/assayCache.ts (72 lines of code) (raw):
import * as fs from "fs";
import * as path from "path";
export class AssayCache {
cacheFolderPath: string;
cacheFilePath: string;
constructor(public cacheName: string, public storagePath: string) {
this.cacheFolderPath = path.join(storagePath, ".cache");
this.cacheFilePath = path.join(this.cacheFolderPath, `${cacheName}.json`);
}
/**
* Adds a value to keys in cache cacheName.
* If an empty string is passed to value, the value at key(s) is deleted from cache.
* @param keys The key(s) of the value.
* @param value The value to store at key(s).
*/
// Caches are stored at
// ../Code/User/globalStorage/mozilla.assay/<cacheName>.cache
// Upon initialization, the extension folder, mozilla.assay,
// may not exist. This function creates that folder and the cache folder
// if they do not exist.
// The folder mozilla.assay will continue to exist after this.
async addToCache(keys: string[], value: any) {
if (!fs.existsSync(this.cacheFolderPath)) {
await fs.promises.mkdir(this.cacheFolderPath, { recursive: true });
}
let cacheFileJSON: any = {};
try {
const cacheFile = await fs.promises.readFile(this.cacheFilePath, "utf-8");
cacheFileJSON = JSON.parse(cacheFile);
} catch (err) {
console.error("No cache file found");
}
let currentLevel = cacheFileJSON;
const levelObjects = [];
for (const key of keys.slice(0, -1)) {
levelObjects.push([currentLevel, key]);
currentLevel = currentLevel[key] = currentLevel[key] || {};
}
if (!value) {
delete currentLevel[keys[keys.length - 1]];
} else {
currentLevel[keys[keys.length - 1]] = value;
}
this.removeEmptyObjectsFromCache(levelObjects);
await fs.promises.writeFile(
this.cacheFilePath,
JSON.stringify(cacheFileJSON, null, 2)
);
}
/**
* Removes a value at keys in cache cacheName.
* @param keys The key(s) of the value.
*/
async removeFromCache(keys: string[]) {
await this.addToCache(keys, "");
}
/**
*
* @param keys The key(s) of the value.
* @returns the value stored at key(s).
*/
async getFromCache(keys: string[] = []) {
if (!fs.existsSync(this.cacheFilePath)) {
return;
}
const cacheFile = await fs.promises.readFile(this.cacheFilePath, "utf-8");
const cacheFileJSON = JSON.parse(cacheFile);
let currentLevel = cacheFileJSON;
for (const key of keys) {
if (!(key in currentLevel)) {
return;
}
currentLevel = currentLevel[key];
}
return currentLevel;
}
/**
* Clears the cache.
* @returns whether the cache was cleared or not.
*/
async clearCache() {
if (fs.existsSync(this.cacheFolderPath)) {
await fs.promises.rm(this.cacheFolderPath, { recursive: true });
return true;
}
return false;
}
/**
* Upon removing comments, we need to remove empty objects from the cache
* so that our file tree indicators are correct.
* @param levelObjects
*/
private removeEmptyObjectsFromCache(levelObjects: any[]) {
while (levelObjects.length > 0) {
const levelObject = levelObjects.pop();
const [parentObject, key] = levelObject;
if (parentObject && Object.keys(parentObject[key]).length === 0) {
delete parentObject[key];
}
}
}
}