Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 05bf6fe

Browse files
committed
feat: create new parser to return readonly map when parsing connecton strings
1 parent cb1e8be commit 05bf6fe

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

src/parse.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import connectionStringParser from './parser/connection-string';
2+
3+
type CoerceType = 'string' | 'number' | 'boolean';
4+
5+
interface SchemaItem<T extends CoerceType = 'string'> {
6+
type?: T;
7+
default?: string | number | boolean;
8+
aliases?: string[];
9+
}
10+
11+
interface SchemaItemBoolean extends SchemaItem<'boolean'> {
12+
type: 'boolean';
13+
default?: boolean;
14+
}
15+
16+
interface SchemaItemString extends SchemaItem<'string'> {
17+
type: 'string';
18+
default?: string;
19+
}
20+
21+
interface SchemaItemNumber extends SchemaItem<'number'> {
22+
type: 'number';
23+
default?: number;
24+
}
25+
26+
type SchemaDefinition = Record<string, SchemaItemString | SchemaItemNumber | SchemaItemBoolean>;
27+
28+
class ConnectionString implements ReadonlyMap<string, string> {
29+
readonly #connectionString: string;
30+
readonly #parsed: ReadonlyMap<string, string>;
31+
constructor(connectionString: string) {
32+
this.#connectionString = connectionString.toString();
33+
const parsed = connectionStringParser(this.#connectionString);
34+
this.#parsed = new Map<string, string>(Object.entries(parsed));
35+
}
36+
37+
get size(): number {
38+
return this.#parsed.size;
39+
}
40+
41+
// it would be really nice to be able to make this a generice (eg: get<string>) and that would then coerce the value
42+
// see typia library for an example of something similar
43+
get(key: string, coerceType: 'boolean'): boolean | undefined;
44+
get(key: string, coerceType: 'number'): number | undefined;
45+
get(key: string, coerceType?: 'string'): string | undefined;
46+
get(key: string, coerceType?: CoerceType): string | number | boolean | undefined {
47+
const val = this.#parsed.get(key);
48+
coerceType ??= 'string';
49+
if (typeof val === 'undefined' || coerceType === 'string') {
50+
return val;
51+
}
52+
switch (coerceType) {
53+
case 'boolean':
54+
// a really liberal interpretation of "false" - "" "0" and "false" are false, everything else is true
55+
return !!val || val.toLowerCase() !== 'false';
56+
case 'number':
57+
return parseInt(val, 10);
58+
default:
59+
throw new TypeError('Coerce type not supported');
60+
}
61+
}
62+
63+
keys(): MapIterator<string> {
64+
return this.#parsed.keys();
65+
}
66+
67+
values(): MapIterator<string> {
68+
return this.#parsed.values();
69+
}
70+
71+
[Symbol.iterator](): MapIterator<[string, string]> {
72+
return this.#parsed[Symbol.iterator]();
73+
}
74+
75+
entries() {
76+
return this.#parsed.entries();
77+
}
78+
79+
toString() {
80+
return this.#connectionString;
81+
}
82+
83+
has(key: string) {
84+
return this.#parsed.has(key.toLowerCase());
85+
}
86+
87+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
88+
forEach(callbackfn: (value: string, key: string, map: ReadonlyMap<string, string>) => void, thisArg?: any): void {
89+
this.#parsed.forEach((value, key) => {
90+
callbackfn.call(thisArg ?? this, value, key, this);
91+
});
92+
}
93+
94+
// a way to extract a formatted object from the connection string
95+
toSchema<T extends SchemaDefinition>(schema: T): { [Property in keyof T]: string | number | boolean } {
96+
return Object.fromEntries(Object.entries(schema).map(([key, { type, default: defaultValue, aliases }]) => {
97+
// try to find the property
98+
const prop = [key, ...aliases ?? []].find((k) => this.has(k));
99+
return [key, prop ? this.get(prop, type) : defaultValue];
100+
}));
101+
}
102+
}
103+
104+
export default function parse(connectionString: string) {
105+
return Object.freeze(new ConnectionString(connectionString));
106+
}

0 commit comments

Comments
 (0)