emmercm/igir

View on GitHub
src/modules/candidates/candidatePostProcessor.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import ProgressBar, { ProgressBarSymbol } from '../../console/progressBar.js';
import DAT from '../../types/dats/dat.js';
import Parent from '../../types/dats/parent.js';
import Options from '../../types/options.js';
import OutputFactory from '../../types/outputFactory.js';
import ReleaseCandidate from '../../types/releaseCandidate.js';
import ROMWithFiles from '../../types/romWithFiles.js';
import Module from '../module.js';

/**
 * Perform any {@link Parent} and {@link ReleaseCandidate} manipulations needed after candidates
 * have had patches attached and have been filtered.
 */
export default class CandidatePostProcessor extends Module {
  private readonly options: Options;

  constructor(options: Options, progressBar: ProgressBar) {
    super(progressBar, CandidatePostProcessor.name);
    this.options = options;
  }

  /**
   * Post-process the candidates.
   */
  process(
    dat: DAT,
    parentsToCandidates: Map<Parent, ReleaseCandidate[]>,
  ): Map<Parent, ReleaseCandidate[]> {
    if (parentsToCandidates.size === 0) {
      this.progressBar.logTrace(`${dat.getNameShort()}: no parents, so no candidates to process`);
      return parentsToCandidates;
    }

    this.progressBar.logTrace(`${dat.getNameShort()}: processing candidates`);
    this.progressBar.setSymbol(ProgressBarSymbol.CANDIDATE_GENERATING);
    this.progressBar.reset(parentsToCandidates.size);

    // Get the output basename of every ROM
    const outputFileBasenames = [...parentsToCandidates.values()]
      .flat()
      .flatMap((releaseCandidate) =>
        releaseCandidate.getRomsWithFiles().map((romWithFiles) => {
          const outputPathParsed = OutputFactory.getPath(
            this.options,
            dat,
            releaseCandidate.getGame(),
            releaseCandidate.getRelease(),
            romWithFiles.getRom(),
            romWithFiles.getInputFile(),
          );
          return outputPathParsed.name + outputPathParsed.ext;
        }),
      );

    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.logTrace(`${dat.getNameShort()}: done processing candidates`);
    return processedCandidates;
  }

  private mapReleaseCandidate(
    dat: DAT,
    releaseCandidate: ReleaseCandidate,
    outputFileBasenames: string[],
  ): ReleaseCandidate {
    const newRomsWithFiles = this.mapRomsWithFiles(
      dat,
      releaseCandidate,
      releaseCandidate.getRomsWithFiles(),
      outputFileBasenames,
    );

    return releaseCandidate.withRomsWithFiles(newRomsWithFiles);
  }

  private mapRomsWithFiles(
    dat: DAT,
    releaseCandidate: ReleaseCandidate,
    romsWithFiles: ROMWithFiles[],
    outputFileBasenames: string[],
  ): 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 = romWithFiles.getOutputFile().withFilePath(newOutputPath);
      return romWithFiles.withOutputFile(newOutputFile);
    });
  }
}