mirror of
https://github.com/koloml/furbooru-tagging-assistant.git
synced 2025-12-24 07:12:57 +00:00
Merge pull request #50 from koloml/feature/moving-import-and-export-to-separate-class
Slightly reduced extension content scripts size by extracting import/export logic into separate class
This commit is contained in:
73
src/lib/extension/EntitiesTransporter.ts
Normal file
73
src/lib/extension/EntitiesTransporter.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import {validateImportedEntity} from "$lib/extension/transporting/validators.js";
|
||||
import {exportEntityToObject} from "$lib/extension/transporting/exporters.js";
|
||||
import StorageEntity from "./base/StorageEntity.js";
|
||||
import {compressToEncodedURIComponent, decompressFromEncodedURIComponent} from "lz-string";
|
||||
|
||||
type EntityConstructor<T extends StorageEntity> =
|
||||
(new (id: string, settings: Record<string, any>) => T)
|
||||
& typeof StorageEntity;
|
||||
|
||||
export default class EntitiesTransporter<EntityType extends StorageEntity> {
|
||||
readonly #targetEntityConstructor: EntityConstructor<EntityType>;
|
||||
|
||||
constructor(entityConstructor: EntityConstructor<EntityType>) {
|
||||
this.#targetEntityConstructor = entityConstructor;
|
||||
}
|
||||
|
||||
importFromJSON(jsonString: string): EntityType {
|
||||
const importedObject = this.#tryParsingAsJSON(jsonString);
|
||||
|
||||
if (!importedObject) {
|
||||
throw new Error('Invalid JSON!');
|
||||
}
|
||||
|
||||
validateImportedEntity(
|
||||
importedObject,
|
||||
this.#targetEntityConstructor._entityName
|
||||
);
|
||||
|
||||
return new this.#targetEntityConstructor(
|
||||
importedObject.id,
|
||||
importedObject
|
||||
);
|
||||
}
|
||||
|
||||
importFromCompressedJSON(compressedJsonString: string): EntityType {
|
||||
return this.importFromJSON(
|
||||
decompressFromEncodedURIComponent(compressedJsonString)
|
||||
)
|
||||
}
|
||||
|
||||
exportToJSON(entityObject: EntityType): string {
|
||||
if (!(entityObject instanceof this.#targetEntityConstructor)) {
|
||||
throw new TypeError('Transporter should be connected to the same entity to export!');
|
||||
}
|
||||
|
||||
const exportableObject = exportEntityToObject(
|
||||
entityObject,
|
||||
this.#targetEntityConstructor._entityName
|
||||
);
|
||||
|
||||
return JSON.stringify(exportableObject, null, 2);
|
||||
}
|
||||
|
||||
exportToCompressedJSON(entityObject: EntityType): string {
|
||||
return compressToEncodedURIComponent(this.exportToJSON(entityObject));
|
||||
}
|
||||
|
||||
#tryParsingAsJSON(jsonString: string): Record<string, any> | null {
|
||||
let jsonObject: Record<string, any> | null = null;
|
||||
|
||||
try {
|
||||
jsonObject = JSON.parse(jsonString);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
if (typeof jsonObject !== "object") {
|
||||
throw new TypeError("Should be an object!");
|
||||
}
|
||||
|
||||
return jsonObject
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import StorageEntity from "$lib/extension/base/StorageEntity.js";
|
||||
import EntitiesController from "$lib/extension/EntitiesController.js";
|
||||
import {compressToEncodedURIComponent, decompressFromEncodedURIComponent} from "lz-string";
|
||||
|
||||
/**
|
||||
* @typedef {Object} MaintenanceProfileSettings
|
||||
@@ -30,26 +29,6 @@ class MaintenanceProfile extends StorageEntity {
|
||||
return super.settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the profile to the formatted JSON.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
toJSON() {
|
||||
return JSON.stringify({
|
||||
v: 1,
|
||||
id: this.id,
|
||||
name: this.settings.name,
|
||||
tags: this.settings.tags,
|
||||
}, null, 2);
|
||||
}
|
||||
|
||||
toCompressedJSON() {
|
||||
return compressToEncodedURIComponent(
|
||||
this.toJSON()
|
||||
);
|
||||
}
|
||||
|
||||
static _entityName = "profiles";
|
||||
|
||||
/**
|
||||
@@ -79,62 +58,6 @@ class MaintenanceProfile extends StorageEntity {
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and import the profile from the JSON.
|
||||
* @param {string} exportedString JSON for profile.
|
||||
* @return {MaintenanceProfile} Maintenance profile imported from the JSON. Note that profile is not automatically
|
||||
* saved.
|
||||
* @throws {Error} When version is unsupported or format is invalid.
|
||||
*/
|
||||
static importFromJSON(exportedString) {
|
||||
let importedObject;
|
||||
|
||||
try {
|
||||
importedObject = JSON.parse(exportedString);
|
||||
} catch (e) {
|
||||
// Error will be sent later, since empty string could be parsed as nothing without raising the error.
|
||||
}
|
||||
|
||||
if (!importedObject) {
|
||||
throw new Error('Invalid JSON!');
|
||||
}
|
||||
|
||||
if (importedObject.v !== 1) {
|
||||
throw new Error('Unsupported version!');
|
||||
}
|
||||
|
||||
if (
|
||||
!importedObject.id
|
||||
|| typeof importedObject.id !== "string"
|
||||
|| !importedObject.name
|
||||
|| typeof importedObject.name !== "string"
|
||||
|| !importedObject.tags
|
||||
|| !Array.isArray(importedObject.tags)
|
||||
) {
|
||||
throw new Error('Invalid profile format detected!');
|
||||
}
|
||||
|
||||
return new MaintenanceProfile(
|
||||
importedObject.id,
|
||||
{
|
||||
name: importedObject.name,
|
||||
tags: importedObject.tags,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and import the profile from the compressed JSON string.
|
||||
* @param {string} compressedString
|
||||
* @return {MaintenanceProfile}
|
||||
* @throws {Error} When version is unsupported or format is invalid.
|
||||
*/
|
||||
static importFromCompressedJSON(compressedString) {
|
||||
return this.importFromJSON(
|
||||
decompressFromEncodedURIComponent(compressedString)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MaintenanceProfile;
|
||||
|
||||
26
src/lib/extension/transporting/exporters.js
Normal file
26
src/lib/extension/transporting/exporters.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @type {Map<string, ((entity: import('../base/StorageEntity.js').default) => Record<string, any>)>}
|
||||
*/
|
||||
const entitiesExporters = new Map([
|
||||
['profiles', /** @param {import('../entities/MaintenanceProfile.js').default} entity */entity => {
|
||||
return {
|
||||
v: 1,
|
||||
id: entity.id,
|
||||
name: entity.settings.name,
|
||||
tags: entity.settings.tags,
|
||||
}
|
||||
}]
|
||||
])
|
||||
|
||||
/**
|
||||
* @param entityInstance
|
||||
* @param {string} entityName
|
||||
* @returns {Record<string, *>}
|
||||
*/
|
||||
export function exportEntityToObject(entityInstance, entityName) {
|
||||
if (!entitiesExporters.has(entityName)) {
|
||||
throw new Error(`Missing exporter for entity: ${entityName}`);
|
||||
}
|
||||
|
||||
return entitiesExporters.get(entityName).call(null, entityInstance);
|
||||
}
|
||||
39
src/lib/extension/transporting/validators.js
Normal file
39
src/lib/extension/transporting/validators.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Map of validators for each entity. Function should throw the error if validation failed.
|
||||
* @type {Map<string, ((importedObject: Object) => void)>}
|
||||
*/
|
||||
const entitiesValidators = new Map([
|
||||
['profiles', importedObject => {
|
||||
if (importedObject.v !== 1) {
|
||||
throw new Error('Unsupported version!');
|
||||
}
|
||||
|
||||
if (
|
||||
!importedObject.id
|
||||
|| typeof importedObject.id !== "string"
|
||||
|| !importedObject.name
|
||||
|| typeof importedObject.name !== "string"
|
||||
|| !importedObject.tags
|
||||
|| !Array.isArray(importedObject.tags)
|
||||
) {
|
||||
throw new Error('Invalid profile format detected!');
|
||||
}
|
||||
}]
|
||||
])
|
||||
|
||||
/**
|
||||
* Validate the structure of the entity.
|
||||
* @param {Object} importedObject Object imported from JSON.
|
||||
* @param {string} entityName Name of the entity to validate. Should be loaded from the entity class.
|
||||
* @throws {Error} Error in case validation failed with the reason stored in the message.
|
||||
*/
|
||||
export function validateImportedEntity(importedObject, entityName) {
|
||||
if (!entitiesValidators.has(entityName)) {
|
||||
console.error(`Trying to validate entity without the validator present! Entity name: ${entityName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
entitiesValidators
|
||||
.get(entityName)
|
||||
.call(null, importedObject);
|
||||
}
|
||||
Reference in New Issue
Block a user