emmercm/igir

View on GitHub
src/modules/patchScanner.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import ProgressBar, { ProgressBarSymbol } from '../console/progressBar.js';
import DriveSemaphore from '../driveSemaphore.js';
import File from '../types/files/file.js';
import { ChecksumBitmask } from '../types/files/fileChecksums.js';
import FileFactory from '../types/files/fileFactory.js';
import Options from '../types/options.js';
import Patch from '../types/patches/patch.js';
import PatchFactory from '../types/patches/patchFactory.js';
import Scanner from './scanner.js';

/**
 * Scan for {@link Patch}es and parse them into the correct supported type.
 */
export default class PatchScanner extends Scanner {
  constructor(options: Options, progressBar: ProgressBar, fileFactory: FileFactory) {
    super(options, progressBar, fileFactory, PatchScanner.name);
  }

  /**
   * Scan & process {@link Patch}es.
   */
  async scan(): Promise<Patch[]> {
    this.progressBar.logTrace('scanning patch files');
    this.progressBar.setSymbol(ProgressBarSymbol.FILE_SCANNING);
    this.progressBar.reset(0);

    const patchFilePaths = await this.options.scanPatchFilesWithoutExclusions((increment) => {
      this.progressBar.incrementTotal(increment);
    });
    this.progressBar.logTrace(
      `found ${patchFilePaths.length.toLocaleString()} patch file${patchFilePaths.length !== 1 ? 's' : ''}`,
    );
    this.progressBar.reset(patchFilePaths.length);

    const patchFiles = await this.getUniqueFilesFromPaths(
      patchFilePaths,
      this.options.getReaderThreads(),
      ChecksumBitmask.NONE,
    );
    this.progressBar.reset(patchFiles.length);

    const patches = (
      await new DriveSemaphore(this.options.getReaderThreads()).map(patchFiles, async (file) => {
        this.progressBar.incrementProgress();
        const waitingMessage = `${file.toString()} ...`;
        this.progressBar.addWaitingMessage(waitingMessage);

        try {
          return await this.patchFromFile(file);
        } catch (error) {
          this.progressBar.logWarn(`${file.toString()}: failed to parse patch: ${error}`);
          return undefined;
        } finally {
          this.progressBar.incrementDone();
          this.progressBar.removeWaitingMessage(waitingMessage);
        }
      })
    ).filter((patch) => patch !== undefined);

    this.progressBar.logTrace('done scanning patch files');
    return patches;
  }

  private async patchFromFile(file: File): Promise<Patch | undefined> {
    const patchForFilename = await PatchFactory.patchFromFilename(file);
    if (patchForFilename) {
      this.progressBar.logTrace(
        `${file.toString()}: found patch by extension: ${typeof patchForFilename}`,
      );
      return patchForFilename;
    }

    const patchForFileContents = await PatchFactory.patchFromFileContents(file);
    if (patchForFileContents) {
      this.progressBar.logTrace(
        `${file.toString()}: found patch by contents: ${typeof patchForFileContents}`,
      );
      return patchForFileContents;
    }

    return undefined;
  }
}