1 | import {Field} from './record';
|
2 |
|
3 | const DEFAULT_FIELD_DELIMITER = ',';
|
4 | const VALID_FIELD_DELIMITERS = [DEFAULT_FIELD_DELIMITER, ';'];
|
5 |
|
6 | export abstract class FieldStringifier {
|
7 | constructor(public readonly fieldDelimiter: string) {}
|
8 |
|
9 | abstract stringify(value?: Field): string;
|
10 |
|
11 | protected isEmpty(value?: Field): boolean {
|
12 | return typeof value === 'undefined' || value === null || value === '';
|
13 | }
|
14 |
|
15 | protected quoteField(field: string): string {
|
16 | return `"${field.replace(/"/g, '""')}"`;
|
17 | }
|
18 | }
|
19 |
|
20 | class DefaultFieldStringifier extends FieldStringifier {
|
21 | stringify(value?: Field): string {
|
22 | if (this.isEmpty(value)) return '';
|
23 | const str = String(value);
|
24 | return this.needsQuote(str) ? this.quoteField(str) : str;
|
25 | }
|
26 |
|
27 | private needsQuote(str: string): boolean {
|
28 | return str.includes(this.fieldDelimiter) || str.includes('\n') || str.includes('"');
|
29 | }
|
30 | }
|
31 |
|
32 | class ForceQuoteFieldStringifier extends FieldStringifier {
|
33 | stringify(value?: Field): string {
|
34 | return this.isEmpty(value) ? '' : this.quoteField(String(value));
|
35 | }
|
36 | }
|
37 |
|
38 | export function createFieldStringifier(fieldDelimiter: string = DEFAULT_FIELD_DELIMITER, alwaysQuote = false) {
|
39 | _validateFieldDelimiter(fieldDelimiter);
|
40 | return alwaysQuote ? new ForceQuoteFieldStringifier(fieldDelimiter) : new DefaultFieldStringifier(fieldDelimiter);
|
41 | }
|
42 |
|
43 | function _validateFieldDelimiter(delimiter: string): void {
|
44 | if (VALID_FIELD_DELIMITERS.indexOf(delimiter) === -1) {
|
45 | throw new Error(`Invalid field delimiter \`${delimiter}\` is specified`);
|
46 | }
|
47 | }
|