@v4fire/core
Version:
V4Fire core library
171 lines (142 loc) • 4.05 kB
text/typescript
/*!
* V4Fire Core
* https://github.com/V4Fire/Core
*
* Released under the MIT license
* https://github.com/V4Fire/Core/blob/master/LICENSE
*/
import type { ClearFilter } from 'core/kv-storage/interface';
import { defaultDataSeparators } from 'core/kv-storage/engines/string/const';
import type { StorageOptions, DataSeparators } from 'core/kv-storage/engines/string/interface';
export default class StringEngine {
/**
* Serialized storage data
*/
get serializedData(): string {
return this.data;
}
/**
* Serialized storage data
*/
protected data: string;
/**
* Separators for keys and values for serialization into a string
*/
protected separators: DataSeparators = defaultDataSeparators;
/**
* Replaces serialized storage data with new ones
*/
// eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures
protected set serializedData(data: string) {
this.data = data;
}
/**
* @param [opts] - additional options
*/
constructor(opts: StorageOptions = {}) {
this.data = opts.data ?? '';
Object.assign(this.separators, opts.separators);
}
/**
* Returns true if a value by the specified key exists in the storage
* @param key
*/
has(key: string): boolean {
return key in this.getDataFromRaw();
}
/**
* Returns a value from the storage by the specified key
* @param key
*/
get(key: string): CanUndef<string> {
return this.getDataFromRaw()[key];
}
/**
* Stores a value to the storage by the specified key
*
* @param key
* @param value
* @throws {TypeError} if forbidden characters are used in the key or value when storing data
*/
set(key: string, value: string): void {
const
separators = Object.values(this.separators),
isForbiddenCharacterUsed = separators.some((el) => key.includes(el) || String(value).includes(el));
if (isForbiddenCharacterUsed) {
throw new TypeError(`The forbidden character used in string storage keys is "${key}", with value "${String(value)}"`);
}
this.updateData({[key]: String(value)});
}
/**
* Removes a value from the storage by the specified key
* @param key
*/
remove(key: string): void {
this.updateData({[key]: undefined});
}
/**
* Returns a list of keys that are stored in the storage
*/
keys(): string[] {
return Object.keys(this.getDataFromRaw());
}
/**
* Clears either the entire data storage or records that match the specified filter
* @param filter
*/
clear(filter?: ClearFilter<string>): void {
if (filter != null) {
const
state = this.getDataFromRaw();
Object.entries(state).forEach(([key, value]) => {
if (filter(String(value), key) === true) {
delete state[key];
}
});
this.overwriteData(state);
} else {
this.overwriteData({});
}
}
/**
* Update the data in the storage with the data passed from the dictionary
* @param data - the dictionary with the data to be added to the storage
*/
protected updateData(data: Dictionary<CanUndef<string>>): void {
const
currentState = this.getDataFromRaw();
Object.entries(data).forEach(([key, value]) => {
if (value === undefined) {
delete currentState[key];
} else {
currentState[key] = value;
}
});
this.overwriteData(currentState);
}
/**
* Overwrites the data in the storage with the new data from the passed dictionary
* @param data - the dictionary with the data to be saved in the storage
*/
protected overwriteData(data: Dictionary<string>): void {
const s = this.separators;
this.serializedData = Object.entries(data)
.map(([key, value]) => `${key}${s.record}${value}`)
.join(s.chunk);
}
/**
* Returns data parsed as a dictionary from the serialized data string
*/
protected getDataFromRaw(): StrictDictionary<string> {
const {serializedData} = this;
if (serializedData === '') {
return {};
}
const s = this.separators;
return serializedData.split(s.chunk).reduce((acc, el) => {
const [key, value] = el.split(s.record);
acc[key] = value;
return acc;
}, {});
}
}