Source: lib/loader.js

/*
 *  MIPS-Simulator : loader.js [Loads file into memory]
 *  Copyright (C)  2017  Progyan Bhattacharya, Bytes Club
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

"use strict";

const fs = require("fs");

/** class: Loader
 * @desc Loads Instruction Contents from File Passed as Parameter
 * @param {string}      file       - Source File Path
 * @param {Object}      flag       - Configuration Object
 * @prop  {boolean}     isSilent   - Omit Error Message to Standard Error
 * @prop  {boolean}     showWarn   - Display Warning instead of Error/Log
 * @prop  {boolean}     showDetail - Display Details of The Error Message
 * @prop  {FileStream}  loader     - FileStream Module of Node.JS for File IO
 * @prop  {string}      target     - Path to Source File
 * @prop  {boolean}     hasError   - True iff Any Error has Occured
 * @prop  {string}      errorText  - The Error Message
 * @prop  {string}      error      - Details of The Error
 * @prop  {string}      content    - File Contents
 */
class Loader extends Object {
    constructor({ file, flag }) {
        super();

        // House-keeing
        if (typeof file === "undefined" || file === null) {
            const err = "No file parameter passed. Expected one!";
            throw err;
        }

        // Member variables
        const flagDefined = typeof flag !== "undefined";
        this.isSilent     = flagDefined && typeof flag.silent === "boolean" ?
                            flag.silent :
                            false;
        this.showWarn     = flagDefined && typeof flag.warning === "boolean" ?
                            flag.warning :
                            true;
        this.showDetail   = flagDefined && typeof flag.details === "boolean" ?
                            flag.details :
                            false;
        this.loader       = fs;
        this.target       = file;
        this.hasError     = false;
        this.errorText    = null;
        this.error        = null;
        this.content      = null;
        this.getContent   = this.getContent.bind(this);
        this.getError     = this.getError.bind(this);
    }

    /** method: load
     * @desc Loads File Contents into Content Property
     * @prop {FileStream} loader - File Loader for IO
     * @prop {string}     file   - Target File Path
    */
    load() {
        const { loader, target: file } = this;

        // Check if file exist on path
        if (! loader.existsSync(file)) {
            this.hasError = true;
            this.errorText = `Fatal Eror: The file ${file} does not exist.` +
                " Make sure you supplied right path.";
            return;
        }

        // Check read permission on the file
        try {
            loader.accessSync(file, loader.constants.R_OK);
        } catch (e) {
            this.hasError = true;
            this.errorText = `Fatal Error: Cannot read from file ${file}.` +
                " Make sure you have READ permission on it.";
            this.error = e.toString();
            return;
        }

        // Read file content
        try {
            this.content = loader.readFileSync(file, "ASCII");
        } catch (e) {
            this.hasError = true;
            this.errorText = `Fatal Error: Failed to read from file ${file}.` +
                " Make sure the file is not corrupted.";
            this.error = e.toString();
            return;
        }
    }

    /** method: getContent
     * @desc Returns File Content to Caller Method
     * @prop {boolean} hasError - If Error has Occured
     * @prop {string}  content  - File Content
     * @return {string} Set of Instructions as String
     */
    getContent() {
        const { hasError, content } = this;

        // Check if error took place
        if (! hasError) {
            return content;
        }

        this.getError();
        process.exit(1);
    }

    /** method: getError
     * @desc Display Error Information if any
     * @prop {boolean} hasError   - True iff Any Error has Occured
     * @prop {boolean} isSilent   - Omit Error Message to Standard Error
     * @prop {boolean} showWarn   - Display Warning instead of Error/Log
     * @prop {boolean} showDetail - Display Details of The Error Message
     * @prop {string}  errorText  - The Error Message
     * @prop {string}  error      - Details of The Error
     */
    getError() {
        const
        { hasError, isSilent, showWarn, showDetail, error, errorText } = this;

        // If error occured
        if (hasError) {
            let errorMsg = errorText;
            if (showDetail && error) {
                errorMsg = `${errorMsg}\n${error}`;
            }
            if (isSilent) {
                if (showWarn) {
                    console.warn(errorMsg);
                } else {
                    console.log(errorMsg);
                }
            } else {
                console.error(errorMsg);
            }
        }
    }
}

module.exports = Loader;