Server/project/src/utils/ImporterUtil.ts

205 lines
6.9 KiB
TypeScript
Raw Normal View History

2023-03-03 15:23:46 +00:00
import { inject, injectable } from "tsyringe";
import { Queue } from "@spt-aki/utils/collections/queue/Queue";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { VFS } from "@spt-aki/utils/VFS";
2023-03-03 15:23:46 +00:00
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/brace-style */
@injectable()
export class ImporterUtil
{
constructor(
@inject("VFS") protected vfs: VFS,
2023-11-13 11:14:58 -05:00
@inject("JsonUtil") protected jsonUtil: JsonUtil,
2023-03-03 15:23:46 +00:00
)
{}
/**
* Load files into js objects recursively (asynchronous)
* @param filepath Path to folder with files
* @returns Promise<T> return T type associated with this class
*/
public async loadRecursiveAsync<T>(
filepath: string,
2023-11-13 11:14:58 -05:00
onReadCallback: (fileWithPath: string, data: string) => void = () =>
{},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
2023-03-03 15:23:46 +00:00
): Promise<T>
{
const result = {} as T;
// get all filepaths
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
// add file content to result
for (const file of files)
{
if (this.vfs.getFileExtension(file) === "json")
{
const filename = this.vfs.stripExtension(file);
const filePathAndName = `${filepath}${file}`;
2023-11-13 11:14:58 -05:00
await this.vfs.readFileAsync(filePathAndName).then((fileData) =>
2023-03-03 15:23:46 +00:00
{
onReadCallback(filePathAndName, fileData);
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
onObjectDeserialized(filePathAndName, fileDeserialized);
result[filename] = fileDeserialized;
});
}
}
// deep tree search
for (const dir of directories)
{
result[dir] = this.loadRecursiveAsync(`${filepath}${dir}/`);
}
// set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result);
2023-11-13 11:14:58 -05:00
const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
2023-03-03 15:23:46 +00:00
for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
{
resEntries[resIdx][1] = resResolved[resIdx];
}
// return the result of all async fetch
return Object.fromEntries(resEntries) as T;
}
/**
* Load files into js objects recursively (synchronous)
* @param filepath Path to folder with files
2023-11-13 11:14:58 -05:00
* @returns
2023-03-03 15:23:46 +00:00
*/
public loadRecursive<T>(
filepath: string,
2023-11-13 11:14:58 -05:00
onReadCallback: (fileWithPath: string, data: string) => void = () =>
{},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
2023-03-03 15:23:46 +00:00
): T
{
const result = {} as T;
// get all filepaths
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
// add file content to result
for (const file of files)
{
if (this.vfs.getFileExtension(file) === "json")
{
const filename = this.vfs.stripExtension(file);
const filePathAndName = `${filepath}${file}`;
const fileData = this.vfs.readFile(filePathAndName);
onReadCallback(filePathAndName, fileData);
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
onObjectDeserialized(filePathAndName, fileDeserialized);
result[filename] = fileDeserialized;
}
}
// deep tree search
for (const dir of directories)
{
result[dir] = this.loadRecursive(`${filepath}${dir}/`);
}
return result;
}
public async loadAsync<T>(
filepath: string,
strippablePath = "",
2023-11-13 11:14:58 -05:00
onReadCallback: (fileWithPath: string, data: string) => void = () =>
{},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
2023-03-03 15:23:46 +00:00
): Promise<T>
{
const directoriesToRead = new Queue<string>();
const filesToProcess = new Queue<VisitNode>();
const promises = new Array<Promise<any>>();
const result = {} as T;
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
2023-11-13 11:14:58 -05:00
directoriesToRead.enqueueAll(directories.map((d) => `${filepath}${d}`));
filesToProcess.enqueueAll(files.map((f) => new VisitNode(filepath, f)));
2023-03-03 15:23:46 +00:00
while (!directoriesToRead.isEmpty())
{
const directory = directoriesToRead.dequeue();
2023-11-13 11:14:58 -05:00
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map((f) => new VisitNode(`${directory}/`, f)));
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map((d) => `${directory}/${d}`));
2023-03-03 15:23:46 +00:00
}
while (!filesToProcess.isEmpty())
{
const fileNode = filesToProcess.dequeue();
if (this.vfs.getFileExtension(fileNode.fileName) === "json")
{
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;
promises.push(
this.vfs.readFileAsync(filePathAndName)
2023-11-13 11:14:58 -05:00
.then(async (fileData) =>
{
2023-03-03 15:23:46 +00:00
onReadCallback(filePathAndName, fileData);
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
})
2023-11-13 11:14:58 -05:00
.then(async (fileDeserialized) =>
{
2023-03-03 15:23:46 +00:00
onObjectDeserialized(filePathAndName, fileDeserialized);
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
2023-11-13 11:14:58 -05:00
}),
2023-03-03 15:23:46 +00:00
);
}
}
2023-11-13 11:14:58 -05:00
await Promise.all(promises).catch((e) => console.error(e));
2023-03-03 15:23:46 +00:00
return result;
}
2023-11-13 11:14:58 -05:00
protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string): void
{
2023-03-03 15:23:46 +00:00
const strippedFinalPath = strippedFilePath.replace(strippablePath, "");
let temp = result;
const propertiesToVisit = strippedFinalPath.split("/");
for (let i = 0; i < propertiesToVisit.length; i++)
{
const property = propertiesToVisit[i];
if (i === (propertiesToVisit.length - 1))
{
temp[property] = fileDeserialized;
}
else
{
if (!temp[property])
2023-11-13 11:14:58 -05:00
{
2023-03-03 15:23:46 +00:00
temp[property] = {};
2023-11-13 11:14:58 -05:00
}
2023-03-03 15:23:46 +00:00
temp = temp[property];
}
}
}
}
class VisitNode
{
constructor(
public filePath: string,
2023-11-13 11:14:58 -05:00
public fileName: string,
)
{}
}