UNPKG

@pujansrt/data-genie

Version:

High performant ETL engine written in TypeScript

146 lines (145 loc) 6.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FixedWidthWriter = void 0; const fs_1 = require("fs"); /** * FixedWidthWriter class for writing data records to a file in a fixed-width format. * Each field's width is predefined, and values are padded or truncated to fit. */ class FixedWidthWriter { /** * Constructs a new FixedWidthWriter. * @param filePath The path to the output file. */ constructor(filePath) { this.fieldWidths = []; this.fieldNames = []; this.hasFieldNamesInFirstRow = false; this.headerWritten = false; this.initializedFieldNames = false; // To track if field names have been determined this.filePath = filePath; this.outputStream = (0, fs_1.createWriteStream)(this.filePath, { encoding: 'utf-8' }); } /** * Sets the fixed widths for each field. This is mandatory before writing. * @param widths A list of numbers, where each number represents the width of a field. * @returns The current FixedWidthWriter instance for chaining. */ setFieldWidths(...widths) { if (widths.some((w) => w <= 0)) { throw new Error('Field widths must be positive numbers.'); } this.fieldWidths = widths; return this; } /** * Sets the names of the fields. If `hasFieldNamesInFirstRow` is true, these names * will be used to write the header row. Otherwise, they define the order of data * extraction from `DataRecord` objects. * @param names A list of string names for the fields. * @returns The current FixedWidthWriter instance for chaining. */ setFieldNames(...names) { this.fieldNames = names; this.initializedFieldNames = true; // Field names are explicitly set return this; } /** * Specifies whether the first row written should be a header row containing the field names. * If true, `setFieldNames` must be called before writing any records. * @param value True to write field names in the first row, false otherwise. * @returns The current FixedWidthWriter instance for chaining. */ setFieldNamesInFirstRow(value) { this.hasFieldNamesInFirstRow = value; return this; } /** * Writes a single data record to the fixed-width file. * @param record The DataRecord object to write. * @returns A Promise that resolves when the record has been written. * @throws Error if field widths are not defined. * @throws Error if `hasFieldNamesInFirstRow` is true but field names are not set. * @throws Error if the number of field names does not match the number of field widths. */ async write(record) { if (this.fieldWidths.length === 0) { throw new Error('Field widths must be defined using setFieldWidths() before writing.'); } // Ensure field names are determined if not explicitly set if (!this.initializedFieldNames) { this.fieldNames = Object.keys(record); this.initializedFieldNames = true; } // Validate that the number of field names matches the number of widths if (this.fieldNames.length !== this.fieldWidths.length) { throw new Error(`Number of field names (${this.fieldNames.length}) must match number of field widths (${this.fieldWidths.length}).`); } // Write header if enabled and not already written if (this.hasFieldNamesInFirstRow && !this.headerWritten) { // If header is expected, field names must have been set explicitly or inferred if (this.fieldNames.length === 0) { throw new Error('Field names must be set using setFieldNames() when hasFieldNamesInFirstRow is true.'); } const headerLine = this.fieldNames.map((name, index) => this.formatField(name, this.fieldWidths[index])).join(''); this.outputStream.write(headerLine + '\n'); this.headerWritten = true; } // Format and write the data record const dataLine = this.fieldNames .map((fieldName, index) => { const value = record[fieldName] !== undefined ? record[fieldName] : ''; // Handle undefined values return this.formatField(value, this.fieldWidths[index]); }) .join(''); return new Promise((resolve, reject) => { this.outputStream.write(dataLine + '\n', (error) => { if (error) { reject(error); } else { resolve(); } }); }); } /** * Writes all data records from an asynchronous iterable to the fixed-width file. * @param records An AsyncIterableIterator of DataRecord objects. * @returns A Promise that resolves when all records have been written. */ async writeAll(records) { for await (const record of records) { await this.write(record); } } /** * Closes the underlying write stream. This should be called when all data has been written. * @returns A Promise that resolves when the stream is closed. */ async close() { return new Promise((resolve) => { this.outputStream.end(() => { resolve(); }); }); } /** * Formats a single field value to fit the specified width. * It truncates if the value is too long and pads with spaces to the right if too short. * @param value The value to format. * @param width The target width for the field. * @returns The formatted string. */ formatField(value, width) { let strValue = String(value); if (strValue.length > width) { return strValue.substring(0, width); // Truncate } else if (strValue.length < width) { return strValue.padEnd(width, ' '); // Pad with spaces } return strValue; } } exports.FixedWidthWriter = FixedWidthWriter;