forked from denodrivers/postgres
-
Notifications
You must be signed in to change notification settings - Fork 0
/
encode.ts
99 lines (85 loc) · 3.01 KB
/
encode.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
function pad(number: number, digits: number): string {
let padded = "" + number;
while (padded.length < digits) {
padded = "0" + padded;
}
return padded;
}
function encodeDate(date: Date): string {
// Construct ISO date
const year = pad(date.getFullYear(), 4);
const month = pad(date.getMonth() + 1, 2);
const day = pad(date.getDate(), 2);
const hour = pad(date.getHours(), 2);
const min = pad(date.getMinutes(), 2);
const sec = pad(date.getSeconds(), 2);
const ms = pad(date.getMilliseconds(), 3);
const encodedDate = `${year}-${month}-${day}T${hour}:${min}:${sec}.${ms}`;
// Construct timezone info
//
// Date.prototype.getTimezoneOffset();
//
// From MDN:
// > The time-zone offset is the difference, in minutes, from local time to UTC.
// > Note that this means that the offset is positive if the local timezone is
// > behind UTC and negative if it is ahead. For example, for time zone UTC+10:00
// > (Australian Eastern Standard Time, Vladivostok Time, Chamorro Standard Time),
// > -600 will be returned.
const offset = date.getTimezoneOffset();
const tzSign = offset > 0 ? "-" : "+";
const absOffset = Math.abs(offset);
const tzHours = pad(Math.floor(absOffset / 60), 2);
const tzMinutes = pad(Math.floor(absOffset % 60), 2);
const encodedTz = `${tzSign}${tzHours}:${tzMinutes}`;
return encodedDate + encodedTz;
}
function escapeArrayElement(value: unknown): string {
// deno-lint-ignore no-explicit-any
let strValue = (value as any).toString();
const escapedValue = strValue.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
return `"${escapedValue}"`;
}
function encodeArray(array: Array<unknown>): string {
let encodedArray = "{";
array.forEach((element, index) => {
if (index > 0) {
encodedArray += ",";
}
if (element === null || typeof element === "undefined") {
encodedArray += "NULL";
} else if (Array.isArray(element)) {
encodedArray += encodeArray(element);
} else if (element instanceof Uint8Array) {
// TODO: it should be encoded as bytea?
throw new Error("Can't encode array of buffers.");
} else {
const encodedElement = encode(element);
encodedArray += escapeArrayElement(encodedElement as string);
}
});
encodedArray += "}";
return encodedArray;
}
function encodeBytes(value: Uint8Array): string {
let hex = Array.from(value)
.map((val) => (val < 10 ? `0${val.toString(16)}` : val.toString(16)))
.join("");
return `\\x${hex}`;
}
export type EncodedArg = null | string | Uint8Array;
export function encode(value: unknown): EncodedArg {
if (value === null || typeof value === "undefined") {
return null;
} else if (value instanceof Uint8Array) {
return encodeBytes(value);
} else if (value instanceof Date) {
return encodeDate(value);
} else if (value instanceof Array) {
return encodeArray(value);
} else if (value instanceof Object) {
return JSON.stringify(value);
} else {
// deno-lint-ignore no-explicit-any
return (value as any).toString();
}
}