Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: eliminate some File & ArchiveEntry promises #612

Merged
merged 4 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/modules/candidateCombiner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default class CandidateCombiner extends Module {
}

// Combine all output ArchiveEntry to a single archive of the DAT name
let outputEntry = await outputFile.withFilePath(dat.getNameShort());
let outputEntry = outputFile.withFilePath(dat.getNameShort());

// If the game has multiple ROMs, then group them in a folder in the archive
if (releaseCandidate.getGame().getRoms().length > 1) {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/candidatePatchGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default class CandidatePatchGenerator extends Module {
// Apply the patch to the appropriate file
if (patch.getCrcBefore() === romWithFiles.getRom().getCrc32()) {
// Attach the patch to the input file
inputFile = await inputFile.withPatch(patch);
inputFile = inputFile.withPatch(patch);

// Build a new output file
const extMatch = romWithFiles.getRom().getName().match(/[^.]+((\.[a-zA-Z0-9]+)+)$/);
Expand Down
77 changes: 36 additions & 41 deletions src/modules/candidatePostProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default class CandidatePostProcessor extends Module {
await this.progressBar.setSymbol(ProgressBarSymbol.GENERATING);
await this.progressBar.reset(parentsToCandidates.size);

// Get the output basename of every ROM
const outputFileBasenames = [...parentsToCandidates.values()]
.flatMap((releaseCandidates) => releaseCandidates)
.flatMap((releaseCandidate) => releaseCandidate.getRomsWithFiles()
Expand All @@ -50,30 +51,27 @@ export default class CandidatePostProcessor extends Module {
return outputPathParsed.name + outputPathParsed.ext;
}));

const processedCandidates = new Map(await Promise.all(
[...parentsToCandidates.entries()]
.map(async ([parent, releaseCandidates]): Promise<[Parent, ReleaseCandidate[]]> => {
const newReleaseCandidates = await Promise.all(
releaseCandidates.map(async (releaseCandidate) => this.mapReleaseCandidate(
dat,
releaseCandidate,
outputFileBasenames,
)),
);
return [parent, newReleaseCandidates];
}),
));
const processedCandidates = new Map([...parentsToCandidates.entries()]
.map(([parent, releaseCandidates]): [Parent, ReleaseCandidate[]] => {
const newReleaseCandidates = releaseCandidates
.map((releaseCandidate) => this.mapReleaseCandidate(
dat,
releaseCandidate,
outputFileBasenames,
));
return [parent, newReleaseCandidates];
}));

this.progressBar.logInfo(`${dat.getNameShort()}: done processing candidates`);
return processedCandidates;
}

private async mapReleaseCandidate(
private mapReleaseCandidate(
dat: DAT,
releaseCandidate: ReleaseCandidate,
outputFileBasenames: string[],
): Promise<ReleaseCandidate> {
const newRomsWithFiles = await this.mapRomsWithFiles(
): ReleaseCandidate {
const newRomsWithFiles = this.mapRomsWithFiles(
dat,
releaseCandidate,
releaseCandidate.getRomsWithFiles(),
Expand All @@ -87,35 +85,32 @@ export default class CandidatePostProcessor extends Module {
);
}

private async mapRomsWithFiles(
private mapRomsWithFiles(
dat: DAT,
releaseCandidate: ReleaseCandidate,
romsWithFiles: ROMWithFiles[],
outputFileBasenames: string[],
): Promise<ROMWithFiles[]> {
return Promise.all(
romsWithFiles.map(async (romWithFiles) => {
const newOutputPath = OutputFactory.getPath(
this.options,
dat,
releaseCandidate.getGame(),
releaseCandidate.getRelease(),
romWithFiles.getRom(),
romWithFiles.getInputFile(),
outputFileBasenames,
).format();
if (newOutputPath === romWithFiles.getOutputFile().getFilePath()) {
return romWithFiles;
}
): ROMWithFiles[] {
return romsWithFiles.map((romWithFiles) => {
const newOutputPath = OutputFactory.getPath(
this.options,
dat,
releaseCandidate.getGame(),
releaseCandidate.getRelease(),
romWithFiles.getRom(),
romWithFiles.getInputFile(),
outputFileBasenames,
).format();
if (newOutputPath === romWithFiles.getOutputFile().getFilePath()) {
return romWithFiles;
}

const newOutputFile = await romWithFiles.getOutputFile()
.withFilePath(newOutputPath);
return new ROMWithFiles(
romWithFiles.getRom(),
romWithFiles.getInputFile(),
newOutputFile,
);
}),
);
const newOutputFile = romWithFiles.getOutputFile().withFilePath(newOutputPath);
return new ROMWithFiles(
romWithFiles.getRom(),
romWithFiles.getInputFile(),
newOutputFile,
);
});
}
}
80 changes: 29 additions & 51 deletions src/types/files/archives/archiveEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,24 @@ import { Readable } from 'stream';
import Constants from '../../../constants.js';
import fsPoly from '../../../polyfill/fsPoly.js';
import Patch from '../../patches/patch.js';
import File from '../file.js';
import File, { FileProps } from '../file.js';
import ROMHeader from '../romHeader.js';
import Archive from './archive.js';

export default class ArchiveEntry<A extends Archive> extends File {
private readonly archive: A;
interface ArchiveEntryProps<A> extends FileProps {
readonly archive: A;
readonly entryPath: string;
}

private readonly entryPath: string;
export default class ArchiveEntry<A extends Archive> extends File implements ArchiveEntryProps<A> {
readonly archive: A;

protected constructor(
/** {@link File} */
filePath: string,
size: number,
crc: string,
crc32WithoutHeader: string,
symlinkSource: string | undefined,
fileHeader: ROMHeader | undefined,
patch: Patch | undefined,
/** {@link ArchiveEntry} */
archive: A,
entryPath: string,
) {
super(
filePath,
size,
crc,
crc32WithoutHeader,
symlinkSource,
fileHeader,
patch,
);
this.archive = archive;
this.entryPath = path.normalize(entryPath);
readonly entryPath: string;

protected constructor(archiveEntryProps: ArchiveEntryProps<A>) {
super(archiveEntryProps);
this.archive = archiveEntryProps.archive;
this.entryPath = path.normalize(archiveEntryProps.entryPath);
}

static async entryOf<A extends Archive>(
Expand All @@ -63,17 +48,17 @@ export default class ArchiveEntry<A extends Archive> extends File {
}
finalCrcWithoutHeader = finalCrcWithoutHeader ?? crc;

return new ArchiveEntry<A>(
archive.getFilePath(),
return new ArchiveEntry<A>({
filePath: archive.getFilePath(),
size,
crc,
finalCrcWithoutHeader,
finalSymlinkSource,
crc32: crc,
crc32WithoutHeader: finalCrcWithoutHeader,
symlinkSource: finalSymlinkSource,
fileHeader,
patch,
archive,
entryPath,
);
});
}

getArchive(): A {
Expand Down Expand Up @@ -135,15 +120,11 @@ export default class ArchiveEntry<A extends Archive> extends File {
return this.archive.extractEntryToStream(this.getEntryPath(), callback);
}

async withFilePath(filePath: string): Promise<ArchiveEntry<Archive>> {
return ArchiveEntry.entryOf(
this.getArchive().withFilePath(filePath),
this.getEntryPath(),
this.getSize(),
this.getCrc32(),
this.getFileHeader(),
this.getPatch(),
);
withFilePath(filePath: string): ArchiveEntry<Archive> {
return new ArchiveEntry({
...this,
archive: this.getArchive().withFilePath(filePath),
});
}

async withEntryPath(entryPath: string): Promise<ArchiveEntry<A>> {
Expand Down Expand Up @@ -176,19 +157,16 @@ export default class ArchiveEntry<A extends Archive> extends File {
);
}

async withPatch(patch: Patch): Promise<ArchiveEntry<A>> {
withPatch(patch: Patch): ArchiveEntry<A> {
if (patch.getCrcBefore() !== this.getCrc32()) {
return this;
}

return ArchiveEntry.entryOf(
this.getArchive(),
this.getEntryPath(),
this.getSize(),
this.getCrc32(),
undefined, // don't allow a file header
return new ArchiveEntry({
...this,
fileHeader: undefined, // don't allow a file header
patch,
);
});
}

toString(): string {
Expand Down
85 changes: 41 additions & 44 deletions src/types/files/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,43 @@ import Cache from '../cache.js';
import Patch from '../patches/patch.js';
import ROMHeader from './romHeader.js';

export default class File {
export interface FileProps {
readonly filePath: string;
readonly size: number;
readonly crc32: string;
readonly crc32WithoutHeader: string;
readonly symlinkSource?: string;
readonly fileHeader?: ROMHeader;
readonly patch?: Patch;
}

export default class File implements FileProps {
private static readonly crc32Cache = new Cache<string, string>(
Constants.FILE_CHECKSUM_CACHE_SIZE,
);

private readonly filePath: string;
readonly filePath: string;

private readonly size: number;
readonly size: number;

private readonly crc32: string;
readonly crc32: string;

private readonly crc32WithoutHeader: string;
readonly crc32WithoutHeader: string;

private readonly symlinkSource?: string;
readonly symlinkSource?: string;

private readonly fileHeader?: ROMHeader;
readonly fileHeader?: ROMHeader;

private readonly patch?: Patch;
readonly patch?: Patch;

protected constructor(
filePath: string,
size: number,
crc: string,
crc32WithoutHeader: string,
symlinkSource?: string,
fileHeader?: ROMHeader,
patch?: Patch,
) {
this.filePath = path.normalize(filePath);
this.size = size;
this.crc32 = crc.toLowerCase().padStart(8, '0');
this.crc32WithoutHeader = crc32WithoutHeader.toLowerCase().padStart(8, '0');
this.symlinkSource = symlinkSource;
this.fileHeader = fileHeader;
this.patch = patch;
protected constructor(fileProps: FileProps) {
this.filePath = path.normalize(fileProps.filePath);
this.size = fileProps.size;
this.crc32 = fileProps.crc32.toLowerCase().padStart(8, '0');
this.crc32WithoutHeader = fileProps.crc32WithoutHeader.toLowerCase().padStart(8, '0');
this.symlinkSource = fileProps.symlinkSource;
this.fileHeader = fileProps.fileHeader;
this.patch = fileProps.patch;
}

static async fileOf(
Expand Down Expand Up @@ -79,15 +81,15 @@ export default class File {
}
finalCrcWithoutHeader = finalCrcWithoutHeader ?? finalCrc;

return new File(
return new File({
filePath,
finalSize,
finalCrc,
finalCrcWithoutHeader,
finalSymlinkSource,
size: finalSize,
crc32: finalCrc,
crc32WithoutHeader: finalCrcWithoutHeader,
symlinkSource: finalSymlinkSource,
fileHeader,
patch,
);
});
}

// Property getters
Expand Down Expand Up @@ -335,14 +337,11 @@ export default class File {
return this.downloadToPath(filePath);
}

async withFilePath(filePath: string): Promise<File> {
return File.fileOf(
withFilePath(filePath: string): File {
return new File({
...this,
filePath,
this.getSize(),
this.getCrc32(),
this.getFileHeader(),
this.getPatch(),
);
});
}

async withFileHeader(fileHeader: ROMHeader): Promise<File> {
Expand All @@ -363,18 +362,16 @@ export default class File {
);
}

async withPatch(patch: Patch): Promise<File> {
withPatch(patch: Patch): File {
if (patch.getCrcBefore() !== this.getCrc32()) {
return this;
}

return File.fileOf(
this.getFilePath(),
this.getSize(),
this.getCrc32(),
undefined, // don't allow a file header
return new File({
...this,
fileHeader: undefined,
patch,
);
});
}

/** *************************
Expand Down
Loading