import {Field} from './record'; const DEFAULT_FIELD_DELIMITER = ','; const VALID_FIELD_DELIMITERS = [DEFAULT_FIELD_DELIMITER, ';']; export abstract class FieldStringifier { constructor(public readonly fieldDelimiter: string) {} abstract stringify(value?: Field): string; protected isEmpty(value?: Field): boolean { return typeof value === 'undefined' || value === null || value === ''; } protected quoteField(field: string): string { return `"${field.replace(/"/g, '""')}"`; } } class DefaultFieldStringifier extends FieldStringifier { stringify(value?: Field): string { if (this.isEmpty(value)) return ''; const str = String(value); return this.needsQuote(str) ? this.quoteField(str) : str; } private needsQuote(str: string): boolean { return str.includes(this.fieldDelimiter) || str.includes('\n') || str.includes('"'); } } class ForceQuoteFieldStringifier extends FieldStringifier { stringify(value?: Field): string { return this.isEmpty(value) ? '' : this.quoteField(String(value)); } } export function createFieldStringifier(fieldDelimiter: string = DEFAULT_FIELD_DELIMITER, alwaysQuote = false) { _validateFieldDelimiter(fieldDelimiter); return alwaysQuote ? new ForceQuoteFieldStringifier(fieldDelimiter) : new DefaultFieldStringifier(fieldDelimiter); } function _validateFieldDelimiter(delimiter: string): void { if (VALID_FIELD_DELIMITERS.indexOf(delimiter) === -1) { throw new Error(`Invalid field delimiter \`${delimiter}\` is specified`); } }