diff --git a/src/utils/errors/file-not-found-error.ts b/src/utils/errors/file-not-found-error.ts new file mode 100644 index 0000000..8ec6a45 --- /dev/null +++ b/src/utils/errors/file-not-found-error.ts @@ -0,0 +1,48 @@ +import { PathLike, existsSync } from "node:fs"; + +/** + * Represents an error that occurs when a specified file cannot be found. + */ +export class FileNotFoundError extends Error { + /** + * Default error message pattern. + */ + private static readonly DEFAULT_FILE_NOT_FOUND_ERROR_MESSAGE_PATTERN = (fileName: string) => fileName ? `Could not find file '${fileName}'.` : "Could not find the specified file."; + + /** + * The name of the file that could not be found. + */ + private readonly _fileName?: string; + + /** + * Constructs a new {@link FileNotFoundError} instance. + * + * @param fileName - The name of the file that could not be found. + * @param message - The error message to display. + * @param options - Optional settings for the error object. + */ + constructor(fileName?: string, message?: string, options?: ErrorOptions) { + super(message ?? FileNotFoundError.DEFAULT_FILE_NOT_FOUND_ERROR_MESSAGE_PATTERN(fileName), options); + + this.name = "FileNotFoundError"; + this._fileName = fileName; + } + + /** + * Gets the name of the file that could not be found. + */ + get fileName(): string | undefined { + return this._fileName; + } + + /** + * Throws a {@link FileNotFoundError} if the specified file does not exist. + * + * @param fileName - The name of the file to check for existence. + */ + static throwIfNotFound(fileName: PathLike): void | never { + if (!existsSync(fileName)) { + throw new FileNotFoundError(String(fileName)); + } + } +} diff --git a/tests/unit/utils/errors.ts/file-not-found-error.spec.ts b/tests/unit/utils/errors.ts/file-not-found-error.spec.ts new file mode 100644 index 0000000..88623ad --- /dev/null +++ b/tests/unit/utils/errors.ts/file-not-found-error.spec.ts @@ -0,0 +1,42 @@ +import { FileNotFoundError } from "@/utils/errors/file-not-found-error"; +import mockFs from "mock-fs"; + +describe("FileNotFoundError", () => { + describe("constructor", () => { + test("initializes with default message if none provided", () => { + const error = new FileNotFoundError("test.txt"); + + expect(error).toBeInstanceOf(FileNotFoundError); + expect(error.name).toBe("FileNotFoundError"); + expect(error.message).toBe("Could not find file 'test.txt'."); + expect(error.fileName).toBe("test.txt"); + }); + + test("initializes with provided message", () => { + const error = new FileNotFoundError("test.txt", "Custom error message"); + + expect(error).toBeInstanceOf(FileNotFoundError); + expect(error.name).toBe("FileNotFoundError"); + expect(error.message).toBe("Custom error message"); + expect(error.fileName).toBe("test.txt"); + }); + }); + + describe("throwIfNotFound", () => { + beforeEach(() => { + mockFs({ "test": "test" }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + test("throws error if file does not exist", () => { + expect(() => FileNotFoundError.throwIfNotFound("test.txt")).toThrow(FileNotFoundError); + }); + + test("does not throw error if file exists", () => { + expect(() => FileNotFoundError.throwIfNotFound("test")).not.toThrow(); + }); + }); +});