Skip to content

Commit ca5dee2

Browse files
committed
feat(quoted_string): add isQuotedString function
1 parent 70e14a9 commit ca5dee2

File tree

3 files changed

+113
-9
lines changed

3 files changed

+113
-9
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,24 @@ assert(isQuotedPair("\\\xFF"));
622622
assertFalse(isQuotedPair("\\"));
623623
```
624624

625+
### isQuotedString
626+
627+
Whether the input is
628+
[quoted-string](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-2).
629+
630+
```ts
631+
import { isQuotedString } from "https://deno.land/x/http_utils@$VERSION/quoted_string.ts";
632+
import {
633+
assert,
634+
assertFalse,
635+
} from "https://deno.land/std@$VERSION/testing/asserts.ts";
636+
637+
assert(isQuotedString(`""`));
638+
assert(isQuotedString(`"qdtext"`));
639+
assert(isQuotedString(`"quoted-pair"`));
640+
assertFalse(isQuotedString(""));
641+
```
642+
625643
## License
626644

627645
Copyright © 2023-present [httpland](https://github.com/httpland).

quoted_string.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
22
// This module is browser compatible.
33

4-
// export function isQuotedString(input: string): input is QuotedString {
5-
// }
6-
74
/**
85
* ```abnf
96
* obs-text = %x80-FF
@@ -33,6 +30,7 @@ export function isQdtext(input: string): boolean {
3330

3431
/**
3532
* ```abnf
33+
* obs-text = %x80-FF
3634
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
3735
* ```
3836
*/
@@ -59,3 +57,37 @@ export type QuotedPair = `\\${string}`;
5957
export function isQuotedPair(input: string): input is QuotedPair {
6058
return reQuotedPair.test(input);
6159
}
60+
61+
/** [quoted-string](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-2). */
62+
export type QuotedString = `"${string}"`;
63+
64+
/**
65+
* ```abnf
66+
* quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
67+
* qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
68+
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
69+
* obs-text = %x80-FF
70+
* ```
71+
*/
72+
const reQuotedString =
73+
/^"(?:[\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*?"$/;
74+
75+
/** Whether the input is [quoted-string](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-2).
76+
*
77+
* @example
78+
* ```ts
79+
* import { isQuotedString } from "https://deno.land/x/http_utils@$VERSION/quoted_string.ts";
80+
* import {
81+
* assert,
82+
* assertFalse,
83+
* } from "https://deno.land/std@$VERSION/testing/asserts.ts";
84+
*
85+
* assert(isQuotedString(`""`));
86+
* assert(isQuotedString(`"qdtext"`));
87+
* assert(isQuotedString(`"quoted-pair"`));
88+
* assertFalse(isQuotedString(""));
89+
* ```
90+
*/
91+
export function isQuotedString(input: string): input is QuotedString {
92+
return reQuotedString.test(input);
93+
}

quoted_string_test.ts

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isQdtext, isQuotedPair } from "./quoted_string.ts";
1+
import { isQdtext, isQuotedPair, isQuotedString } from "./quoted_string.ts";
22
import {
33
assert,
44
assertEquals,
@@ -19,6 +19,18 @@ function charRange(start: string, end: string): string[] {
1919
);
2020
}
2121

22+
function slashed(input: string): `\\${string}` {
23+
return `\\${input}`;
24+
}
25+
26+
function quoted(input: string): `"${string}"` {
27+
return `"${input}"`;
28+
}
29+
30+
function next(input: string): string {
31+
return String.fromCharCode(input.charCodeAt(0) + 1);
32+
}
33+
2234
const vchars = charRange("\x21", "\x7E");
2335
const obsTexts = charRange("\x80", "\xFF");
2436

@@ -73,10 +85,6 @@ describe("isQdtext", () => {
7385
});
7486

7587
describe("isQuotedPair", () => {
76-
function slashed(input: string): `\\${string}` {
77-
return `\\${input}`;
78-
}
79-
8088
it("should return true", () => {
8189
const table: string[] = [
8290
"\\\t",
@@ -97,7 +105,7 @@ describe("isQuotedPair", () => {
97105
...charRange("\x00", "\x08").map(slashed), // \x09 is "\t"
98106
...charRange("\x10", "\x19").map(slashed), // \x20 is " "
99107
"\\\x7F",
100-
"\\" + "\xFF" + 1,
108+
"\\" + next("\xFF"),
101109

102110
"\\\x21\x21",
103111
];
@@ -107,3 +115,49 @@ describe("isQuotedPair", () => {
107115
});
108116
});
109117
});
118+
119+
describe("isQuotedString", () => {
120+
it("should return true", () => {
121+
const table: string[] = [
122+
quoted(""),
123+
quoted("\t"),
124+
quoted(" "),
125+
quoted("\x21"),
126+
...charRange("\x23", "\x5B").map(quoted),
127+
...charRange("\x5D", "\x7E").map(quoted),
128+
...obsTexts.map(quoted),
129+
quoted(slashed("\t")),
130+
quoted(slashed(" ")),
131+
...vchars.map(slashed).map(quoted),
132+
...obsTexts.map(slashed).map(quoted),
133+
134+
quoted(vchars.map(slashed).join("")),
135+
quoted(obsTexts.map(slashed).join("")),
136+
quoted(obsTexts.join("")),
137+
quoted(charRange("\x23", "\x5B").join()),
138+
quoted(charRange("\x23", "\x5B").join() + vchars.map(slashed).join("")),
139+
];
140+
141+
table.forEach((input) => {
142+
assert(isQuotedString(input));
143+
});
144+
});
145+
146+
it("should return false", () => {
147+
const table: string[] = [
148+
"",
149+
"''",
150+
quoted("\\"),
151+
quoted("\t\\"),
152+
quoted(next("\xFF")),
153+
quoted("あ"),
154+
quoted(charRange(next("\xFF"), next(next("\xFF"))).join()),
155+
quoted(vchars.join("")),
156+
obsTexts.map(slashed).join(""),
157+
];
158+
159+
table.forEach((input) => {
160+
assertFalse(isQuotedString(input));
161+
});
162+
});
163+
});

0 commit comments

Comments
 (0)