mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
BREAKING(csv): remove ParseError
(#5405)
* initial commit * update --------- Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
This commit is contained in:
parent
13e23ab0e3
commit
edd21d5ae5
151
csv/_io.ts
151
csv/_io.ts
@ -101,7 +101,9 @@ export async function parseRecord(
|
||||
const col = codePointLength(
|
||||
fullLine.slice(0, fullLine.length - line.slice(j).length),
|
||||
);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_BARE_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createBareQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
}
|
||||
recordBuffer += field;
|
||||
@ -141,7 +143,9 @@ export async function parseRecord(
|
||||
const col = codePointLength(
|
||||
fullLine.slice(0, fullLine.length - line.length - quoteLen),
|
||||
);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
} else if (line.length > 0 || !reader.isEOF()) {
|
||||
// Hit end of line (copy all data so far).
|
||||
@ -154,7 +158,9 @@ export async function parseRecord(
|
||||
// Abrupt end of file (EOF or error).
|
||||
if (!options.lazyQuotes) {
|
||||
const col = codePointLength(fullLine);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
fieldIndexes.push(recordBuffer.length);
|
||||
break parseField;
|
||||
@ -164,7 +170,9 @@ export async function parseRecord(
|
||||
// Abrupt end of file (EOF on error).
|
||||
if (!options.lazyQuotes) {
|
||||
const col = codePointLength(fullLine);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
fieldIndexes.push(recordBuffer.length);
|
||||
break parseField;
|
||||
@ -181,129 +189,20 @@ export async function parseRecord(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ParseError is returned for parsing errors.
|
||||
* Line numbers are 1-indexed and columns are 0-indexed.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts
|
||||
* import { parse, ParseError } from "@std/csv/parse";
|
||||
* import { assertEquals } from "@std/assert";
|
||||
*
|
||||
* try {
|
||||
* parse(`a "word","b"`);
|
||||
* } catch (error) {
|
||||
* if (error instanceof ParseError) {
|
||||
* assertEquals(error.message, `parse error on line 1, column 2: bare " in non-quoted-field`);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class ParseError extends SyntaxError {
|
||||
/**
|
||||
* Line where the record starts.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts
|
||||
* import { parse, ParseError } from "@std/csv/parse";
|
||||
* import { assertEquals } from "@std/assert";
|
||||
*
|
||||
* try {
|
||||
* parse(`a "word","b"`);
|
||||
* } catch (error) {
|
||||
* if (error instanceof ParseError) {
|
||||
* assertEquals(error.startLine, 1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
startLine: number;
|
||||
/**
|
||||
* Line where the error occurred.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts
|
||||
* import { parse, ParseError } from "@std/csv/parse";
|
||||
* import { assertEquals } from "@std/assert";
|
||||
*
|
||||
* try {
|
||||
* parse(`a "word","b"`);
|
||||
* } catch (error) {
|
||||
* if (error instanceof ParseError) {
|
||||
* assertEquals(error.line, 1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
line: number;
|
||||
/**
|
||||
* Column (rune index) where the error occurred.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts
|
||||
* import { parse, ParseError } from "@std/csv/parse";
|
||||
* import { assertEquals } from "@std/assert";
|
||||
*
|
||||
* try {
|
||||
* parse(`a "word","b"`);
|
||||
* } catch (error) {
|
||||
* if (error instanceof ParseError) {
|
||||
* assertEquals(error.column, 2);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
column: number | null;
|
||||
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts
|
||||
* import { parse, ParseError } from "@std/csv/parse";
|
||||
* import { assertEquals } from "@std/assert";
|
||||
*
|
||||
* try {
|
||||
* parse(`a "word","b"`);
|
||||
* } catch (error) {
|
||||
* if (error instanceof ParseError) {
|
||||
* assertEquals(error.message, `parse error on line 1, column 2: bare " in non-quoted-field`);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param start Line where the record starts
|
||||
* @param line Line where the error occurred
|
||||
* @param column Column The index where the error occurred
|
||||
* @param message Error message
|
||||
*/
|
||||
constructor(
|
||||
start: number,
|
||||
line: number,
|
||||
column: number | null,
|
||||
message: string,
|
||||
) {
|
||||
super();
|
||||
this.startLine = start;
|
||||
this.column = column;
|
||||
this.line = line;
|
||||
|
||||
if (message === ERR_FIELD_COUNT) {
|
||||
this.message = `record on line ${line}: ${message}`;
|
||||
} else if (start !== line) {
|
||||
this.message =
|
||||
`record on line ${start}; parse error on line ${line}, column ${column}: ${message}`;
|
||||
} else {
|
||||
this.message =
|
||||
`parse error on line ${line}, column ${column}: ${message}`;
|
||||
}
|
||||
}
|
||||
export function createBareQuoteErrorMessage(
|
||||
start: number,
|
||||
line: number,
|
||||
column: number,
|
||||
) {
|
||||
return `record on line ${start}; parse error on line ${line}, column ${column}: bare " in non-quoted-field`;
|
||||
}
|
||||
export function createQuoteErrorMessage(
|
||||
start: number,
|
||||
line: number,
|
||||
column: number,
|
||||
) {
|
||||
return `record on line ${start}; parse error on line ${line}, column ${column}: extraneous or missing " in quoted-field`;
|
||||
}
|
||||
|
||||
export const ERR_BARE_QUOTE = 'bare " in non-quoted-field';
|
||||
export const ERR_QUOTE = 'extraneous or missing " in quoted-field';
|
||||
export const ERR_INVALID_DELIM = "Invalid Delimiter";
|
||||
export const ERR_FIELD_COUNT = "wrong number of fields";
|
||||
|
||||
export function convertRowToObject(
|
||||
row: string[],
|
||||
|
31
csv/parse.ts
31
csv/parse.ts
@ -3,18 +3,15 @@
|
||||
|
||||
import {
|
||||
convertRowToObject,
|
||||
ERR_BARE_QUOTE,
|
||||
ERR_FIELD_COUNT,
|
||||
ERR_INVALID_DELIM,
|
||||
ERR_QUOTE,
|
||||
ParseError,
|
||||
createBareQuoteErrorMessage,
|
||||
createQuoteErrorMessage,
|
||||
type ParseResult,
|
||||
type ReadOptions,
|
||||
type RecordWithColumn,
|
||||
} from "./_io.ts";
|
||||
import { codePointLength } from "./_shared.ts";
|
||||
|
||||
export { ParseError, type ParseResult, type RecordWithColumn };
|
||||
export type { ParseResult, RecordWithColumn };
|
||||
|
||||
const BYTE_ORDER_MARK = "\ufeff";
|
||||
|
||||
@ -112,7 +109,9 @@ class Parser {
|
||||
const col = codePointLength(
|
||||
fullLine.slice(0, fullLine.length - line.slice(j).length),
|
||||
);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_BARE_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createBareQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
}
|
||||
recordBuffer += field;
|
||||
@ -152,7 +151,9 @@ class Parser {
|
||||
const col = codePointLength(
|
||||
fullLine.slice(0, fullLine.length - line.length - quoteLen),
|
||||
);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
} else if (line.length > 0 || !(this.#isEOF())) {
|
||||
// Hit end of line (copy all data so far).
|
||||
@ -165,7 +166,9 @@ class Parser {
|
||||
// Abrupt end of file (EOF or error).
|
||||
if (!this.#options.lazyQuotes) {
|
||||
const col = codePointLength(fullLine);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
fieldIndexes.push(recordBuffer.length);
|
||||
break parseField;
|
||||
@ -175,7 +178,9 @@ class Parser {
|
||||
// Abrupt end of file (EOF on error).
|
||||
if (!this.#options.lazyQuotes) {
|
||||
const col = codePointLength(fullLine);
|
||||
throw new ParseError(startLine + 1, lineIndex, col, ERR_QUOTE);
|
||||
throw new SyntaxError(
|
||||
createQuoteErrorMessage(startLine + 1, lineIndex, col),
|
||||
);
|
||||
}
|
||||
fieldIndexes.push(recordBuffer.length);
|
||||
break parseField;
|
||||
@ -209,7 +214,7 @@ class Parser {
|
||||
INVALID_RUNE.includes(options.comment)) ||
|
||||
options.separator === options.comment
|
||||
) {
|
||||
throw new Error(ERR_INVALID_DELIM);
|
||||
throw new Error("Invalid Delimiter");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
@ -232,7 +237,9 @@ class Parser {
|
||||
|
||||
if (lineResult.length > 0) {
|
||||
if (_nbFields && _nbFields !== lineResult.length) {
|
||||
throw new ParseError(lineIndex, lineIndex, null, ERR_FIELD_COUNT);
|
||||
throw new SyntaxError(
|
||||
`record on line ${lineIndex}: wrong number of fields`,
|
||||
);
|
||||
}
|
||||
result.push(lineResult);
|
||||
}
|
||||
|
@ -1,13 +1,7 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
import { CsvParseStream } from "./parse_stream.ts";
|
||||
import type { CsvParseStreamOptions } from "./parse_stream.ts";
|
||||
import { ERR_QUOTE, ParseError } from "./_io.ts";
|
||||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
assertRejects,
|
||||
assertStringIncludes,
|
||||
} from "@std/assert";
|
||||
import { assertEquals, assertRejects } from "@std/assert";
|
||||
import type { AssertTrue, IsExact } from "@std/testing/types";
|
||||
import { fromFileUrl, join } from "@std/path";
|
||||
import { delay } from "@std/async/delay";
|
||||
@ -48,12 +42,11 @@ Deno.test({
|
||||
const reader = readable.getReader();
|
||||
assertEquals(await reader.read(), { done: false, value: ["id", "name"] });
|
||||
assertEquals(await reader.read(), { done: false, value: ["1", "foo"] });
|
||||
const error = await assertRejects(() => reader.read());
|
||||
assert(error instanceof ParseError);
|
||||
assertEquals(error.startLine, 4);
|
||||
assertEquals(error.line, 5);
|
||||
assertEquals(error.column, 0);
|
||||
assertStringIncludes(error.message, ERR_QUOTE);
|
||||
await assertRejects(
|
||||
() => reader.read(),
|
||||
SyntaxError,
|
||||
`record on line 4; parse error on line 5, column 0: extraneous or missing " in quoted-field`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assert, assertEquals, assertThrows } from "@std/assert";
|
||||
import { parse, ParseError, type ParseOptions } from "./parse.ts";
|
||||
import { parse, type ParseOptions } from "./parse.ts";
|
||||
import type { AssertTrue, IsExact } from "@std/testing/types";
|
||||
|
||||
const BYTE_ORDER_MARK = "\ufeff";
|
||||
@ -193,7 +193,7 @@ Deno.test({
|
||||
const input = `a""b,c`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'parse error on line 1, column 1: bare " in non-quoted-field',
|
||||
);
|
||||
},
|
||||
@ -204,7 +204,7 @@ Deno.test({
|
||||
const input = `a,b,🐱"`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'parse error on line 1, column 5: bare " in non-quoted-field',
|
||||
);
|
||||
},
|
||||
@ -223,7 +223,7 @@ Deno.test({
|
||||
const input = `a "word","b"`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'parse error on line 1, column 2: bare " in non-quoted-field',
|
||||
);
|
||||
},
|
||||
@ -234,7 +234,7 @@ Deno.test({
|
||||
const input = `"a word",b"`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'parse error on line 1, column 10: bare " in non-quoted-field',
|
||||
);
|
||||
},
|
||||
@ -245,7 +245,7 @@ Deno.test({
|
||||
const input = `"a "word","b"`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
`parse error on line 1, column 3: extraneous or missing " in quoted-field`,
|
||||
);
|
||||
},
|
||||
@ -256,7 +256,7 @@ Deno.test({
|
||||
const input = "a,b,c\nd,e";
|
||||
assertThrows(
|
||||
() => parse(input, { fieldsPerRecord: 0 }),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
"record on line 2: wrong number of fields",
|
||||
);
|
||||
},
|
||||
@ -267,7 +267,7 @@ Deno.test({
|
||||
const input = `a,b,c`;
|
||||
assertThrows(
|
||||
() => parse(input, { fieldsPerRecord: 2 }),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
"record on line 1: wrong number of fields",
|
||||
);
|
||||
},
|
||||
@ -384,7 +384,7 @@ Deno.test({
|
||||
const input = 'a,"b\nc"d,e';
|
||||
assertThrows(
|
||||
() => parse(input, { fieldsPerRecord: 2 }),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'record on line 1; parse error on line 2, column 1: extraneous or missing " in quoted-field',
|
||||
);
|
||||
},
|
||||
@ -395,7 +395,7 @@ Deno.test({
|
||||
const input = 'a,b\n"d\n\n,e';
|
||||
assertThrows(
|
||||
() => parse(input, { fieldsPerRecord: 2 }),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'record on line 2; parse error on line 5, column 0: extraneous or missing " in quoted-field',
|
||||
);
|
||||
},
|
||||
@ -438,7 +438,7 @@ Deno.test({
|
||||
const input = '"field"\r\r';
|
||||
assertThrows(
|
||||
() => parse(input, { fieldsPerRecord: 2 }),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
'parse error on line 1, column 6: extraneous or missing " in quoted-field',
|
||||
);
|
||||
},
|
||||
@ -581,7 +581,7 @@ Deno.test({
|
||||
const input = '"foo"bar"\r\n';
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
`parse error on line 1, column 4: extraneous or missing " in quoted-field`,
|
||||
);
|
||||
},
|
||||
@ -616,7 +616,7 @@ Deno.test({
|
||||
const input = `"""""""`;
|
||||
assertThrows(
|
||||
() => parse(input),
|
||||
ParseError,
|
||||
SyntaxError,
|
||||
`parse error on line 1, column 7: extraneous or missing " in quoted-field`,
|
||||
);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user