Skip to content

Commit

Permalink
feat!: parseCSVFromString now return a promise
Browse files Browse the repository at this point in the history
  • Loading branch information
Th1nkK1D committed May 17, 2024
1 parent aee8964 commit 0f951e4
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 60 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const userTable = Table({
// Get type-safe data from the URL
const usersFromUrl = await parseCSVFromUrl('some-url-to/data.csv', userTable);
// Or from string
const usersFromString = parseCSVFromString('name,age\na,27', userTable);
const usersFromString = await parseCSVFromString('name,age\na,27', userTable);

// Can also infer row type from the table schema
type User = RowType<typeof userTable>;
Expand Down Expand Up @@ -195,7 +195,7 @@ const userTable = Table({
// role: "Admin" | "Guest";
// }[]
const usersFromUrl = await parseCSVFromUrl('some-url-to/data.csv', userTable);
const usersFromString = parseCSVFromString('name,age\na,27', userTable);
const usersFromString = await parseCSVFromString('name,age\na,27', userTable);
```

The `CSVFetcherOptions` can be supplied to the `parseCSVFromUrl()` and `CSVParserOptions` can be supplied to the `parseCSVFromString()`.
Expand All @@ -207,7 +207,7 @@ const usersFromUrl = await parseCSVFromUrl('some-url-to/data.csv', {
// CSVFetcherOptions
});

const usersFromString = parseCSVFromString('name,age\na,27', {
const usersFromString = await parseCSVFromString('name,age\na,27', {
// CSVParserOptions
});
```
Expand Down
38 changes: 20 additions & 18 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,30 @@ export function parseCSVFromString<
csvString: string,
columnsSchema: C,
options: CSVParserOptions = {},
): Static<C>[] {
const { trim, includeUnknownColumns } = {
...defaultCSVParserOptions,
...options,
};

const outputSchema = Type.Array(columnsSchema);
): Promise<Static<C>[]> {
return new Promise((resolve, reject) => {
const { trim, includeUnknownColumns } = {
...defaultCSVParserOptions,
...options,
};

const data = Value.Convert(
outputSchema,
csvParse(csvString, processRow(trim)),
);
const outputSchema = Type.Array(columnsSchema);

if (!includeUnknownColumns) {
Value.Clean(outputSchema, data);
}
const data = Value.Convert(
outputSchema,
csvParse(csvString, processRow(trim)),
);

if (!Value.Check(outputSchema, data)) {
throw Error(formatParsingError(Value.Errors(outputSchema, data)));
}
if (!includeUnknownColumns) {
Value.Clean(outputSchema, data);
}

return data;
if (!Value.Check(outputSchema, data)) {
reject(Error(formatParsingError(Value.Errors(outputSchema, data))));
} else {
resolve(data);
}
});
}

const processRow = (trim: boolean) => (obj: Record<any, string>) =>
Expand Down
80 changes: 41 additions & 39 deletions tests/parser/parse-csv-from-string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ describe('parseCSVFromString', () => {
value: Column.String(),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\na\n"with\nnewlint"', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\na\n"with\nnewlint"', table);
expect(res).toEqual([{ value: 'a' }, { value: 'with\nnewlint' }]);
});

Expand All @@ -22,8 +22,8 @@ describe('parseCSVFromString', () => {
value: Column.Number(),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\n100\n-5\n4.2', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\n100\n-5\n4.2', table);
expect(res).toEqual([{ value: 100 }, { value: -5 }, { value: 4.2 }]);
});

Expand All @@ -39,8 +39,8 @@ describe('parseCSVFromString', () => {
value: Column.Boolean(),
});

it('should parse valid value', () => {
const res = parseCSVFromString(
it('should parse valid value', async () => {
const res = await parseCSVFromString(
'value\ntrue\nTRUE\nTrue\n1\nfalse\nFALSE\nFalse\n0',
table,
);
Expand Down Expand Up @@ -68,8 +68,8 @@ describe('parseCSVFromString', () => {
value: Column.Date(),
});

it('should parse valid value (ISO format)', () => {
const res = parseCSVFromString('value\n1996-11-13', table);
it('should parse valid value (ISO format)', async () => {
const res = await parseCSVFromString('value\n1996-11-13', table);
expect(res).toEqual([{ value: new Date('1996-11-13') }]);
});

Expand All @@ -85,8 +85,8 @@ describe('parseCSVFromString', () => {
value: Column.OneOf(['a', 1]),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\na\n1', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\na\n1', table);
expect(res).toEqual([{ value: 'a' }, { value: 1 }]);
});

Expand All @@ -102,13 +102,13 @@ describe('parseCSVFromString', () => {
value: Column.OptionalString(),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\na\n"with\nnewlint"', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\na\n"with\nnewlint"', table);
expect(res).toEqual([{ value: 'a' }, { value: 'with\nnewlint' }]);
});

it('should parse as null if empty', () => {
const res = parseCSVFromString('value\n\n', table);
it('should parse as null if empty', async () => {
const res = await parseCSVFromString('value\n\n', table);
expect(res).toEqual([{ value: null }]);
});
});
Expand All @@ -118,13 +118,13 @@ describe('parseCSVFromString', () => {
value: Column.OptionalNumber(),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\n100\n-5\n4.2', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\n100\n-5\n4.2', table);
expect(res).toEqual([{ value: 100 }, { value: -5 }, { value: 4.2 }]);
});

it('should parse as null if empty', () => {
const res = parseCSVFromString('value\n\n', table);
it('should parse as null if empty', async () => {
const res = await parseCSVFromString('value\n\n', table);
expect(res).toEqual([{ value: null }]);
});

Expand All @@ -137,8 +137,8 @@ describe('parseCSVFromString', () => {
value: Column.OptionalBoolean(),
});

it('should parse valid value', () => {
const res = parseCSVFromString(
it('should parse valid value', async () => {
const res = await parseCSVFromString(
'value\ntrue\nTRUE\nTrue\n1\nfalse\nFALSE\nFalse\n0',
table,
);
Expand All @@ -154,8 +154,8 @@ describe('parseCSVFromString', () => {
]);
});

it('should parse as null if empty', () => {
const res = parseCSVFromString('value\n\n', table);
it('should parse as null if empty', async () => {
const res = await parseCSVFromString('value\n\n', table);
expect(res).toEqual([{ value: null }]);
});

Expand All @@ -168,13 +168,13 @@ describe('parseCSVFromString', () => {
value: Column.OptionalDate(),
});

it('should parse valid value (ISO format)', () => {
const res = parseCSVFromString('value\n1996-11-13', table);
it('should parse valid value (ISO format)', async () => {
const res = await parseCSVFromString('value\n1996-11-13', table);
expect(res).toEqual([{ value: new Date('1996-11-13') }]);
});

it('should parse as null if empty', () => {
const res = parseCSVFromString('value\n\n', table);
it('should parse as null if empty', async () => {
const res = await parseCSVFromString('value\n\n', table);
expect(res).toEqual([{ value: null }]);
});

Expand All @@ -187,13 +187,13 @@ describe('parseCSVFromString', () => {
value: Column.OptionalOneOf(['a', 1]),
});

it('should parse valid value', () => {
const res = parseCSVFromString('value\na\n1', table);
it('should parse valid value', async () => {
const res = await parseCSVFromString('value\na\n1', table);
expect(res).toEqual([{ value: 'a' }, { value: 1 }]);
});

it('should parse as null if empty', () => {
const res = parseCSVFromString('value\n\n', table);
it('should parse as null if empty', async () => {
const res = await parseCSVFromString('value\n\n', table);
expect(res).toEqual([{ value: null }]);
});

Expand All @@ -206,26 +206,28 @@ describe('parseCSVFromString', () => {
value: Column.String(),
});

it('should trim value before parsing by default', () => {
const res = parseCSVFromString('value\n hi ', table);
it('should trim value before parsing by default', async () => {
const res = await parseCSVFromString('value\n hi ', table);
expect(res).toEqual([{ value: 'hi' }]);
});

it('should not trim if option is set to false', () => {
const res = parseCSVFromString('value\n hi ', table, { trim: false });
it('should not trim if option is set to false', async () => {
const res = await parseCSVFromString('value\n hi ', table, {
trim: false,
});
expect(res).toEqual([{ value: ' hi ' }]);
});

it('should not include unknown columns by default', () => {
const res = parseCSVFromString('value,unknown\na,b', table);
it('should not include unknown columns by default', async () => {
const res = await parseCSVFromString('value,unknown\na,b', table);
expect(res).toEqual([{ value: 'a' }]);
});

it('should include unknown columns if set to false', () => {
const res = parseCSVFromString('value,unknown\na,b', table, {
it('should include unknown columns if set to false', async () => {
const res = await parseCSVFromString('value,unknown\na,b', table, {
includeUnknownColumns: true,
});
// @ts-ignore
// @ts-expect-error expects mismatch table schema
expect(res).toEqual([{ value: 'a', unknown: 'b' }]);
});
});
Expand Down

0 comments on commit 0f951e4

Please sign in to comment.