Skip to content

Commit 33afdfe

Browse files
authored
feat(encoding/csv): sync parse (#2491)
1 parent f693ad6 commit 33afdfe

File tree

3 files changed

+468
-309
lines changed

3 files changed

+468
-309
lines changed

encoding/csv.ts

Lines changed: 26 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,18 @@
22
// https://github.com/golang/go/blob/master/LICENSE
33
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
44

5+
// This module is browser compatible.
6+
57
/** Port of the Go
68
* [encoding/csv](https://github.com/golang/go/blob/go1.12.5/src/encoding/csv/)
79
* library.
810
*
911
* @module
1012
*/
1113

12-
import { BufReader } from "../io/buffer.ts";
13-
import { TextProtoReader } from "../textproto/mod.ts";
14-
import { StringReader } from "../io/readers.ts";
1514
import { assert } from "../_util/assert.ts";
16-
import {
17-
ERR_FIELD_COUNT,
18-
ERR_INVALID_DELIM,
19-
ParseError,
20-
readRecord,
21-
} from "./csv/_io.ts";
22-
import type { LineReader, ReadOptions } from "./csv/_io.ts";
15+
import type { ReadOptions } from "./csv/_io.ts";
16+
import { Parser } from "./csv/_parser.ts";
2317

2418
export {
2519
ERR_BARE_QUOTE,
@@ -38,111 +32,6 @@ export type {
3832
StringifyOptions,
3933
} from "./csv_stringify.ts";
4034

41-
class TextProtoLineReader implements LineReader {
42-
#tp: TextProtoReader;
43-
constructor(bufReader: BufReader) {
44-
this.#tp = new TextProtoReader(bufReader);
45-
}
46-
47-
async readLine() {
48-
let line: string;
49-
const r = await this.#tp.readLine();
50-
if (r === null) return null;
51-
line = r;
52-
53-
// For backwards compatibility, drop trailing \r before EOF.
54-
if (
55-
(await this.isEOF()) && line.length > 0 && line[line.length - 1] === "\r"
56-
) {
57-
line = line.substring(0, line.length - 1);
58-
}
59-
60-
// Normalize \r\n to \n on all input lines.
61-
if (
62-
line.length >= 2 &&
63-
line[line.length - 2] === "\r" &&
64-
line[line.length - 1] === "\n"
65-
) {
66-
line = line.substring(0, line.length - 2);
67-
line = line + "\n";
68-
}
69-
70-
return line;
71-
}
72-
73-
async isEOF() {
74-
return (await this.#tp.r.peek(0)) === null;
75-
}
76-
}
77-
78-
const INVALID_RUNE = ["\r", "\n", '"'];
79-
80-
function chkOptions(opt: ReadOptions): void {
81-
if (!opt.separator) {
82-
opt.separator = ",";
83-
}
84-
if (!opt.trimLeadingSpace) {
85-
opt.trimLeadingSpace = false;
86-
}
87-
if (
88-
INVALID_RUNE.includes(opt.separator) ||
89-
(typeof opt.comment === "string" && INVALID_RUNE.includes(opt.comment)) ||
90-
opt.separator === opt.comment
91-
) {
92-
throw new Error(ERR_INVALID_DELIM);
93-
}
94-
}
95-
96-
/**
97-
* Parse the CSV from the `reader` with the options provided and return `string[][]`.
98-
*
99-
* @param reader provides the CSV data to parse
100-
* @param opt controls the parsing behavior
101-
*/
102-
export async function readMatrix(
103-
reader: BufReader,
104-
opt: ReadOptions = {
105-
separator: ",",
106-
trimLeadingSpace: false,
107-
lazyQuotes: false,
108-
},
109-
): Promise<string[][]> {
110-
const result: string[][] = [];
111-
let _nbFields: number | undefined;
112-
let lineResult: string[];
113-
let first = true;
114-
let lineIndex = 0;
115-
chkOptions(opt);
116-
117-
const lineReader = new TextProtoLineReader(reader);
118-
for (;;) {
119-
const r = await readRecord(lineIndex, lineReader, opt);
120-
if (r === null) break;
121-
lineResult = r;
122-
lineIndex++;
123-
// If fieldsPerRecord is 0, Read sets it to
124-
// the number of fields in the first record
125-
if (first) {
126-
first = false;
127-
if (opt.fieldsPerRecord !== undefined) {
128-
if (opt.fieldsPerRecord === 0) {
129-
_nbFields = lineResult.length;
130-
} else {
131-
_nbFields = opt.fieldsPerRecord;
132-
}
133-
}
134-
}
135-
136-
if (lineResult.length > 0) {
137-
if (_nbFields && _nbFields !== lineResult.length) {
138-
throw new ParseError(lineIndex, lineIndex, null, ERR_FIELD_COUNT);
139-
}
140-
result.push(lineResult);
141-
}
142-
}
143-
return result;
144-
}
145-
14635
/**
14736
* Parse the CSV string/buffer with the options provided.
14837
*
@@ -173,46 +62,43 @@ export interface ParseOptions extends ReadOptions {
17362
/**
17463
* Csv parse helper to manipulate data.
17564
* Provides an auto/custom mapper for columns.
176-
* @param input Input to parse. Can be a string or BufReader.
65+
* @param input Input to parse.
17766
* @param opt options of the parser.
17867
* @returns If you don't provide `opt.skipFirstRow` and `opt.columns`, it returns `string[][]`.
17968
* If you provide `opt.skipFirstRow` or `opt.columns`, it returns `Record<string, unkown>[]`.
18069
*/
181-
export async function parse(
182-
input: string | BufReader,
183-
): Promise<string[][]>;
184-
export async function parse(
185-
input: string | BufReader,
70+
export function parse(
71+
input: string,
72+
): string[][];
73+
export function parse(
74+
input: string,
18675
opt: Omit<ParseOptions, "columns" | "skipFirstRow">,
187-
): Promise<string[][]>;
188-
export async function parse(
189-
input: string | BufReader,
76+
): string[][];
77+
export function parse(
78+
input: string,
19079
opt: Omit<ParseOptions, "columns"> & {
19180
columns: string[] | ColumnOptions[];
19281
},
193-
): Promise<Record<string, unknown>[]>;
194-
export async function parse(
195-
input: string | BufReader,
82+
): Record<string, unknown>[];
83+
export function parse(
84+
input: string,
19685
opt: Omit<ParseOptions, "skipFirstRow"> & {
19786
skipFirstRow: true;
19887
},
199-
): Promise<Record<string, unknown>[]>;
200-
export async function parse(
201-
input: string | BufReader,
88+
): Record<string, unknown>[];
89+
export function parse(
90+
input: string,
20291
opt: ParseOptions,
203-
): Promise<string[][] | Record<string, unknown>[]>;
204-
export async function parse(
205-
input: string | BufReader,
92+
): string[][] | Record<string, unknown>[];
93+
export function parse(
94+
input: string,
20695
opt: ParseOptions = {
20796
skipFirstRow: false,
20897
},
209-
): Promise<string[][] | Record<string, unknown>[]> {
210-
let r: string[][];
211-
if (input instanceof BufReader) {
212-
r = await readMatrix(input, opt);
213-
} else {
214-
r = await readMatrix(new BufReader(new StringReader(input)), opt);
215-
}
98+
): string[][] | Record<string, unknown>[] {
99+
const parser = new Parser(opt);
100+
const r = parser.parse(input);
101+
216102
if (opt.skipFirstRow || opt.columns) {
217103
let headers: ColumnOptions[] = [];
218104
let i = 0;

0 commit comments

Comments
 (0)