85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import fs from 'fs'
|
|
import pathlib from 'path'
|
|
import * as json from './json-utils'
|
|
|
|
export class FSKeyValueCache<T extends Object> {
|
|
private _initialized = false
|
|
|
|
public constructor(private _filepath: string) {}
|
|
|
|
public async init(): Promise<void> {
|
|
if (this._initialized) {
|
|
return
|
|
}
|
|
const dirname = pathlib.dirname(this._filepath)
|
|
if (!fs.existsSync(dirname)) {
|
|
await fs.promises.mkdir(dirname, { recursive: true })
|
|
}
|
|
if (!fs.existsSync(this._filepath)) {
|
|
await this._writeJSON(this._filepath, {})
|
|
}
|
|
this._initialized = true
|
|
}
|
|
|
|
public async sync<K extends keyof T>(
|
|
key: K,
|
|
value: T[K] | undefined,
|
|
prompt: (initial?: T[K]) => Promise<T[K]>
|
|
): Promise<T[K]> {
|
|
await this.init()
|
|
if (value) {
|
|
await this.set(key, value)
|
|
return value
|
|
}
|
|
const data = await this.get(key)
|
|
const newValue = await prompt(data)
|
|
await this.set(key, newValue)
|
|
return newValue
|
|
}
|
|
|
|
public async has<K extends keyof T>(key: K): Promise<boolean> {
|
|
await this.init()
|
|
const data = await this._readJSON(this._filepath)
|
|
return data[key] !== undefined
|
|
}
|
|
|
|
public async get<K extends keyof T>(key: K): Promise<T[K] | undefined> {
|
|
await this.init()
|
|
const data: T = await this._readJSON(this._filepath)
|
|
return data[key]
|
|
}
|
|
|
|
public async set<K extends keyof T>(key: K, value: T[K]): Promise<void> {
|
|
await this.init()
|
|
const data: T = await this._readJSON(this._filepath)
|
|
data[key] = value
|
|
return this._writeJSON(this._filepath, data)
|
|
}
|
|
|
|
public async rm<K extends keyof T>(key: K): Promise<void> {
|
|
await this.init()
|
|
const data: T = await this._readJSON(this._filepath)
|
|
delete data[key]
|
|
return this._writeJSON(this._filepath, data)
|
|
}
|
|
|
|
public async clear(): Promise<void> {
|
|
await this.init()
|
|
return this._writeJSON(this._filepath, {})
|
|
}
|
|
|
|
private _writeJSON = (filepath: string, data: any) => {
|
|
const fileContent = JSON.stringify(data, null, 2)
|
|
return fs.promises.writeFile(filepath, fileContent)
|
|
}
|
|
|
|
private _readJSON = async (filepath: string) => {
|
|
const fileContent = await fs.promises.readFile(filepath, 'utf8')
|
|
const parseResult = json.safeParseJson(fileContent)
|
|
if (!parseResult.success) {
|
|
throw new Error(`Failed to parse JSON file at ${filepath}: ${parseResult.error.message}`)
|
|
}
|
|
return parseResult.data as T
|
|
}
|
|
}
|