Skip to content

Commit

Permalink
[#27] Accept recordDelimiter for csv stringifier
Browse files Browse the repository at this point in the history
  • Loading branch information
ryu1kn committed Jun 18, 2019
1 parent 5c12dd7 commit f31beb5
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 22 deletions.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,13 @@ console.log(csvStringifier.stringifyRecords(records));

Default: `,`. Only either comma `,` or semicolon `;` is allowed.

* encoding `<string>` (optional)
* recordDelimiter `<string>` (optional)

Default: `utf8`.
Default: `\n`. Only either comma `\n` or semicolon `\r\n` is allowed.

* newline `<string>` (optional)
* encoding `<string>` (optional)

Default: `lf`. Possible values: `lf`, `crlf`.
Default: `utf8`.

* append `<boolean>` (optional)

Expand Down Expand Up @@ -175,13 +175,13 @@ console.log(csvStringifier.stringifyRecords(records));

Default: `,`. Only either comma `,` or semicolon `;` is allowed.

* encoding `<string>` (optional)
* recordDelimiter `<string>` (optional)

Default: `utf8`.
Default: `\n`. Only either comma `\n` or semicolon `\r\n` is allowed.

* newline `<string>` (optional)
* encoding `<string>` (optional)

Default: `lf`. Possible values: `lf`, `crlf`.
Default: `utf8`.

* append `<boolean>` (optional)

Expand Down Expand Up @@ -222,6 +222,10 @@ console.log(csvStringifier.stringifyRecords(records));

Default: `,`. Only either comma `,` or semicolon `;` is allowed.

* recordDelimiter `<string>` (optional)

Default: `\n`. Only either comma `\n` or semicolon `\r\n` is allowed.

##### Returns:

* `<ObjectCsvStringifier>`
Expand Down Expand Up @@ -255,6 +259,10 @@ console.log(csvStringifier.stringifyRecords(records));

Default: `,`. Only either comma `,` or semicolon `;` is allowed.

* recordDelimiter `<string>` (optional)

Default: `\n`. Only either comma `\n` or semicolon `\r\n` is allowed.

##### Returns:

* `<ArrayCsvStringifier>`
Expand Down
19 changes: 17 additions & 2 deletions src/lib/csv-stringifier-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@ import {ObjectStringifierHeader} from './record';
const DEFAULT_FIELD_DELIMITER = ',';
const VALID_FIELD_DELIMITERS = [DEFAULT_FIELD_DELIMITER, ';'];

const DEFAULT_RECORD_DELIMITER = '\n';
const VALID_RECORD_DELIMITERS = [DEFAULT_RECORD_DELIMITER, '\r\n'];

export interface ArrayCsvStringifierParams {
header?: string[];
fieldDelimiter?: string;
recordDelimiter?: string;
}

export interface ObjectCsvStringifierParams {
header: ObjectStringifierHeader;
fieldDelimiter?: string;
recordDelimiter?: string;
}

export class CsvStringifierFactory {

createArrayCsvStringifier(params: ArrayCsvStringifierParams) {
const fieldDelimiter = params.fieldDelimiter || DEFAULT_FIELD_DELIMITER;
const recordDelimiter = params.recordDelimiter || DEFAULT_RECORD_DELIMITER;
_validateFieldDelimiter(fieldDelimiter);
_validateRecordDelimiter(recordDelimiter);
const fieldStringifier = new FieldStringifier(fieldDelimiter);
return new ArrayCsvStringifier(fieldStringifier, fieldDelimiter, params.header);
return new ArrayCsvStringifier(fieldStringifier, fieldDelimiter, recordDelimiter, params.header);
}

createObjectCsvStringifier(params: ObjectCsvStringifierParams) {
const fieldDelimiter = params.fieldDelimiter || DEFAULT_FIELD_DELIMITER;
const recordDelimiter = params.recordDelimiter || DEFAULT_RECORD_DELIMITER;
_validateFieldDelimiter(fieldDelimiter);
_validateRecordDelimiter(recordDelimiter);
const fieldStringifier = new FieldStringifier(fieldDelimiter);
return new ObjectCsvStringifier(fieldStringifier, fieldDelimiter, params.header);
return new ObjectCsvStringifier(fieldStringifier, fieldDelimiter, recordDelimiter, params.header);
}

}
Expand All @@ -39,3 +48,9 @@ function _validateFieldDelimiter(delimiter: string): void {
throw new Error(`Invalid field delimiter \`${delimiter}\` is specified`);
}
}

function _validateRecordDelimiter(delimiter: string): void {
if (VALID_RECORD_DELIMITERS.indexOf(delimiter) === -1) {
throw new Error(`Invalid record delimiter \`${delimiter}\` is specified`);
}
}
8 changes: 4 additions & 4 deletions src/lib/csv-stringifiers/abstract.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {FieldStringifier} from '../field-stringifier';
import {Field} from '../record';

const RECORD_DELIMITER = '\n';

export abstract class CsvStringifier<T> {
private readonly fieldStringifier: FieldStringifier;
private readonly fieldDelimiter: string;
private readonly recordDelimiter: string;

constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string) {
constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string, recordDelimiter: string) {
this.fieldStringifier = fieldStringifier;
this.fieldDelimiter = fieldDelimiter;
this.recordDelimiter = recordDelimiter;
}

getHeaderString(): string | null {
Expand All @@ -19,7 +19,7 @@ export abstract class CsvStringifier<T> {

stringifyRecords(records: IterableIterator<T> | T[]): string {
const csvLines = Array.from(records, record => this.getCsvLine(this.getRecordAsArray(record)));
return csvLines.join(RECORD_DELIMITER) + RECORD_DELIMITER;
return csvLines.join(this.recordDelimiter) + this.recordDelimiter;
}

protected abstract getRecordAsArray(_record: T): Field[];
Expand Down
4 changes: 2 additions & 2 deletions src/lib/csv-stringifiers/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {Field} from '../record';
export class ArrayCsvStringifier extends CsvStringifier<Field[]> {
private readonly header?: string[];

constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string, header?: string[]) {
super(fieldStringifier, fieldDelimiter);
constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string, recordDelimiter: string, header?: string[]) {
super(fieldStringifier, fieldDelimiter, recordDelimiter);
this.header = header;
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib/csv-stringifiers/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {ObjectMap} from '../lang';
export class ObjectCsvStringifier extends CsvStringifier<ObjectMap<Field>> {
private readonly header: ObjectStringifierHeader;

constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string, header: ObjectStringifierHeader) {
super(fieldStringifier, fieldDelimiter);
constructor(fieldStringifier: FieldStringifier, fieldDelimiter: string, recordDelimiter: string, header: ObjectStringifierHeader) {
super(fieldStringifier, fieldDelimiter, recordDelimiter);
this.header = header;
}

Expand Down
8 changes: 6 additions & 2 deletions src/lib/csv-writer-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface ArrayCsvWriterParams {
path: string;
header?: string[];
fieldDelimiter?: string;
recordDelimiter?: string;
encoding?: string;
append?: boolean;
}
Expand All @@ -14,6 +15,7 @@ export interface ObjectCsvWriterParams {
path: string;
header: ObjectStringifierHeader;
fieldDelimiter?: string;
recordDelimiter?: string;
encoding?: string;
append?: boolean;
}
Expand All @@ -28,15 +30,17 @@ export class CsvWriterFactory {
createArrayCsvWriter(params: ArrayCsvWriterParams) {
const csvStringifier = this.csvStringifierFactory.createArrayCsvStringifier({
header: params.header,
fieldDelimiter: params.fieldDelimiter
fieldDelimiter: params.fieldDelimiter,
recordDelimiter: params.recordDelimiter
});
return new CsvWriter(csvStringifier, params.path, params.encoding, params.append);
}

createObjectCsvWriter(params: ObjectCsvWriterParams) {
const csvStringifier = this.csvStringifierFactory.createObjectCsvStringifier({
header: params.header,
fieldDelimiter: params.fieldDelimiter
fieldDelimiter: params.fieldDelimiter,
recordDelimiter: params.recordDelimiter
});
return new CsvWriter(csvStringifier, params.path, params.encoding, params.append);
}
Expand Down
8 changes: 8 additions & 0 deletions src/test/csv-stringifiers/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ describe('ArrayCsvStringifier', () => {
});
});

describe('When record delimiter is neither LF nor CR+LF', () => {
it('throws an exception', () => {
assert.throws(() => {
createArrayCsvStringifier({recordDelimiter: '\r'});
});
});
});

describe('When records input is an iterable other than an array', () => {
const stringifier = createArrayCsvStringifier({
header: ['TITLE_A', 'TITLE_B']
Expand Down
11 changes: 11 additions & 0 deletions src/test/csv-stringifiers/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ describe('ObjectCsvStringifier', () => {
});
});

describe('When record delimiter is neither LF nor CR+LF', () => {
it('throws an exception', () => {
assert.throws(() => {
createObjectCsvStringifier({
header: ['FIELD_A', 'FIELD_B'],
recordDelimiter: '\r'
});
});
});
});

describe('When records input is an iterable other than an array', () => {
const stringifier = createObjectCsvStringifier({
header: ['FIELD_A', 'FIELD_B']
Expand Down
16 changes: 15 additions & 1 deletion src/test/write-array-records.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,24 @@ describe('Write array records into CSV', () => {
fieldDelimiter: ';'
});

it('writes to a file with the specified encoding', () => {
it('uses semicolon instead of comma to separate fields', () => {
return writer.writeRecords(records).then(() => {
assertFile(filePath, 'NAME;LANGUAGE\nBob;French\nMary;English\n');
});
});
});

describe('When newline is specified', () => {
const filePath = makeFilePath('newline');
const writer = createArrayCsvWriter({
path: filePath,
recordDelimiter: '\r\n'
});

it('writes to a file with the specified newline character', () => {
return writer.writeRecords(records).then(() => {
assertFile(filePath, 'Bob,French\r\nMary,English\r\n');
});
});
});
});
17 changes: 16 additions & 1 deletion src/test/write-object-records.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,25 @@ describe('Write object records into CSV', () => {
fieldDelimiter: ';'
});

it('writes to a file with the specified encoding', () => {
it('uses semicolon instead of comma to separate fields', () => {
return writer.writeRecords(records).then(() => {
assertFile(filePath, 'NAME;LANGUAGE\nBob;French\nMary;English\n');
});
});
});

describe('When newline is specified', () => {
const filePath = makeFilePath('newline');
const writer = createObjectCsvWriter({
path: filePath,
header: ['name', 'lang'],
recordDelimiter: '\r\n'
});

it('writes to a file with the specified newline character', () => {
return writer.writeRecords(records).then(() => {
assertFile(filePath, 'Bob,French\r\nMary,English\r\n');
});
});
});
});

0 comments on commit f31beb5

Please sign in to comment.