Skip to content

Commit b4ff54a

Browse files
committed
BigInt support on unmarshalling
Signed-off-by: moznion <moznion@mail.moznion.net>
1 parent e75b02a commit b4ff54a

File tree

4 files changed

+139
-59
lines changed

4 files changed

+139
-59
lines changed

lib/dynamodb_item_field.ts

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import ts from 'typescript';
2-
import { DynamodbPrimitiveTypes, dynamodbPrimitiveValueToJSValueConvertingOp } from './dynamodb_primitive_types';
2+
import {
3+
DynamodbPrimitiveTypeKinds,
4+
DynamodbPrimitiveType,
5+
dynamodbPrimitiveValueToJSValueConvertingOp,
6+
} from './dynamodb_primitive_types';
37
import { warn } from './logger';
48

59
export interface DynamodbItemField {
@@ -8,20 +12,20 @@ export interface DynamodbItemField {
812
}
913

1014
export class PrimitiveField implements DynamodbItemField {
11-
constructor(private readonly fieldName: string, private readonly fieldType: DynamodbPrimitiveTypes) {}
15+
constructor(private readonly fieldName: string, private readonly fieldType: DynamodbPrimitiveType) {}
1216

1317
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined {
14-
const toStr = this.fieldType === DynamodbPrimitiveTypes.Number ? '.toString()' : '';
18+
const toStr = this.fieldType.kind === DynamodbPrimitiveTypeKinds.Number ? '.toString()' : '';
1519

1620
return ts.factory.createPropertyAssignment(
1721
this.fieldName,
1822
ts.factory.createObjectLiteralExpression(
1923
[
2024
ts.factory.createPropertyAssignment(
21-
this.fieldType,
25+
this.fieldType.kind,
2226
ts.factory.createIdentifier(
2327
`${argName}.${this.fieldName}${toStr}` +
24-
(this.fieldType === DynamodbPrimitiveTypes.Null ? ' === null' : ''),
28+
(this.fieldType.kind === DynamodbPrimitiveTypeKinds.Null ? ' === null' : ''),
2529
),
2630
),
2731
],
@@ -35,17 +39,17 @@ export class PrimitiveField implements DynamodbItemField {
3539
return ts.factory.createPropertyAssignment(
3640
this.fieldName,
3741
ts.factory.createIdentifier(
38-
`${valueConvertingOp.leftOp}${argName}['${this.fieldName}']?.${this.fieldType}${valueConvertingOp.rightOp}`,
42+
`${valueConvertingOp.leftOp}${argName}['${this.fieldName}']?.${this.fieldType.kind}${valueConvertingOp.rightOp}`,
3943
),
4044
);
4145
}
4246
}
4347

4448
export class ArrayField implements DynamodbItemField {
45-
constructor(readonly fieldName: string, readonly valueType: DynamodbPrimitiveTypes) {}
49+
constructor(readonly fieldName: string, readonly valueType: DynamodbPrimitiveType) {}
4650

4751
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined {
48-
const toStr = this.valueType === DynamodbPrimitiveTypes.Number ? '.toString()' : '';
52+
const toStr = this.valueType.kind === DynamodbPrimitiveTypeKinds.Number ? '.toString()' : '';
4953

5054
return ts.factory.createPropertyAssignment(
5155
this.fieldName,
@@ -54,7 +58,7 @@ export class ArrayField implements DynamodbItemField {
5458
ts.factory.createPropertyAssignment(
5559
'L',
5660
ts.factory.createIdentifier(
57-
`${argName}.${this.fieldName}.map(v => new Object({${this.valueType}: v${toStr}}))`,
61+
`${argName}.${this.fieldName}.map(v => new Object({${this.valueType.kind}: v${toStr}}))`,
5862
),
5963
),
6064
],
@@ -68,7 +72,7 @@ export class ArrayField implements DynamodbItemField {
6872
return ts.factory.createPropertyAssignment(
6973
this.fieldName,
7074
ts.factory.createIdentifier(
71-
`${argName}['${this.fieldName}']?.L?.map(v => ${valueConvertingOp.leftOp}v.${this.valueType}${valueConvertingOp.rightOp})`,
75+
`${argName}['${this.fieldName}']?.L?.map(v => ${valueConvertingOp.leftOp}v.${this.valueType.kind}${valueConvertingOp.rightOp})`,
7276
),
7377
);
7478
}
@@ -77,15 +81,15 @@ export class ArrayField implements DynamodbItemField {
7781
export class SetField implements DynamodbItemField {
7882
constructor(
7983
readonly fieldName: string,
80-
readonly valueType: DynamodbPrimitiveTypes,
84+
readonly valueType: DynamodbPrimitiveType,
8185
readonly shouldLenientTypeCheck: boolean,
8286
) {}
8387

8488
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined {
8589
if (
86-
this.valueType !== DynamodbPrimitiveTypes.String &&
87-
this.valueType !== DynamodbPrimitiveTypes.Number &&
88-
this.valueType !== DynamodbPrimitiveTypes.Binary
90+
this.valueType.kind !== DynamodbPrimitiveTypeKinds.String &&
91+
this.valueType.kind !== DynamodbPrimitiveTypeKinds.Number &&
92+
this.valueType.kind !== DynamodbPrimitiveTypeKinds.Binary
8993
) {
9094
const msg = `a type parameter of the Set type property "${this.fieldName}" is unsupported one`;
9195
if (this.shouldLenientTypeCheck) {
@@ -94,14 +98,14 @@ export class SetField implements DynamodbItemField {
9498
}
9599
throw new Error(msg);
96100
}
97-
const mapToStr = this.valueType === DynamodbPrimitiveTypes.Number ? '.map(v => `${v}`)' : '';
101+
const mapToStr = this.valueType.kind === DynamodbPrimitiveTypeKinds.Number ? '.map(v => v.toString())' : '';
98102

99103
return ts.factory.createPropertyAssignment(
100104
this.fieldName,
101105
ts.factory.createObjectLiteralExpression(
102106
[
103107
ts.factory.createPropertyAssignment(
104-
this.valueType + 'S',
108+
this.valueType.kind + 'S',
105109
ts.factory.createIdentifier(`Array.from(${argName}.${this.fieldName}.values())${mapToStr}`),
106110
),
107111
],
@@ -115,7 +119,7 @@ export class SetField implements DynamodbItemField {
115119
return ts.factory.createPropertyAssignment(
116120
this.fieldName,
117121
ts.factory.createIdentifier(
118-
`${argName}['${this.fieldName}']?.${this.valueType}S?.map(v => ${valueConvertingOp.leftOp}v${valueConvertingOp.rightOp})`,
122+
`${argName}['${this.fieldName}']?.${this.valueType.kind}S?.map(v => ${valueConvertingOp.leftOp}v${valueConvertingOp.rightOp})`,
119123
),
120124
);
121125
}
@@ -124,13 +128,13 @@ export class SetField implements DynamodbItemField {
124128
export class MapField implements DynamodbItemField {
125129
constructor(
126130
readonly fieldName: string,
127-
readonly keyValueType: DynamodbPrimitiveTypes,
128-
readonly valueType: DynamodbPrimitiveTypes,
131+
readonly keyValueType: DynamodbPrimitiveType,
132+
readonly valueType: DynamodbPrimitiveType,
129133
readonly shouldLenientTypeCheck: boolean,
130134
) {}
131135

132136
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined {
133-
if (this.keyValueType !== DynamodbPrimitiveTypes.String) {
137+
if (this.keyValueType.kind !== DynamodbPrimitiveTypeKinds.String) {
134138
const msg = `a Map type property "${this.fieldName}" has non-string key type`;
135139
if (this.shouldLenientTypeCheck) {
136140
warn(msg);
@@ -139,7 +143,7 @@ export class MapField implements DynamodbItemField {
139143
throw new Error(msg);
140144
}
141145

142-
const toStr = this.valueType === DynamodbPrimitiveTypes.Number ? '.toString()' : '';
146+
const toStr = this.valueType.kind === DynamodbPrimitiveTypeKinds.Number ? '.toString()' : '';
143147
const recordIdent = ts.factory.createIdentifier('m');
144148

145149
return ts.factory.createPropertyAssignment(
@@ -168,9 +172,10 @@ export class MapField implements DynamodbItemField {
168172
ts.SyntaxKind.EqualsToken,
169173
ts.factory.createObjectLiteralExpression([
170174
ts.factory.createPropertyAssignment(
171-
this.valueType,
175+
this.valueType.kind,
172176
ts.factory.createIdentifier(
173-
`kv[1]${toStr}` + (this.valueType === DynamodbPrimitiveTypes.Null ? ' === null' : ''),
177+
`kv[1]${toStr}` +
178+
(this.valueType.kind === DynamodbPrimitiveTypeKinds.Null ? ' === null' : ''),
174179
),
175180
),
176181
]),
@@ -214,7 +219,7 @@ export class MapField implements DynamodbItemField {
214219
recordIdent,
215220
ts.factory.createBlock([
216221
ts.factory.createIdentifier(
217-
`${valueIdent.text}.set(k, ${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType}${valueConvertingOp.rightOp})`,
222+
`${valueIdent.text}.set(k, ${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType.kind}${valueConvertingOp.rightOp})`,
218223
),
219224
] as unknown as ts.Statement[]),
220225
),
@@ -227,13 +232,13 @@ export class MapField implements DynamodbItemField {
227232
export class KeyValuePairMapField implements DynamodbItemField {
228233
constructor(
229234
readonly fieldName: string,
230-
readonly keyValueType: DynamodbPrimitiveTypes,
231-
readonly valueType: DynamodbPrimitiveTypes,
235+
readonly keyValueType: DynamodbPrimitiveType,
236+
readonly valueType: DynamodbPrimitiveType,
232237
readonly shouldLenientTypeCheck: boolean,
233238
) {}
234239

235240
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined {
236-
if (this.keyValueType !== DynamodbPrimitiveTypes.String) {
241+
if (this.keyValueType.kind !== DynamodbPrimitiveTypeKinds.String) {
237242
const msg = `a Map type property "${this.fieldName}" has non-string key type`;
238243
if (this.shouldLenientTypeCheck) {
239244
warn(msg);
@@ -242,7 +247,7 @@ export class KeyValuePairMapField implements DynamodbItemField {
242247
throw new Error(msg);
243248
}
244249

245-
const toStr = this.valueType === DynamodbPrimitiveTypes.Number ? '.toString()' : '';
250+
const toStr = this.valueType.kind === DynamodbPrimitiveTypeKinds.Number ? '.toString()' : '';
246251
const recordIdent = ts.factory.createIdentifier('m');
247252

248253
return ts.factory.createPropertyAssignment(
@@ -270,10 +275,10 @@ export class KeyValuePairMapField implements DynamodbItemField {
270275
ts.SyntaxKind.EqualsToken,
271276
ts.factory.createObjectLiteralExpression([
272277
ts.factory.createPropertyAssignment(
273-
this.valueType,
278+
this.valueType.kind,
274279
ts.factory.createIdentifier(
275280
`${argName}.${this.fieldName}[k]${toStr}` +
276-
(this.valueType === DynamodbPrimitiveTypes.Null ? ' === null' : ''),
281+
(this.valueType.kind === DynamodbPrimitiveTypeKinds.Null ? ' === null' : ''),
277282
),
278283
),
279284
]),
@@ -316,7 +321,7 @@ export class KeyValuePairMapField implements DynamodbItemField {
316321
ts.factory.createIdentifier(`${valueIdent.text}[k]`),
317322
ts.SyntaxKind.EqualsToken,
318323
ts.factory.createIdentifier(
319-
`${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType}${valueConvertingOp.rightOp}`,
324+
`${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType.kind}${valueConvertingOp.rightOp}`,
320325
),
321326
),
322327
] as unknown as ts.Statement[]),

lib/dynamodb_primitive_types.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
import ts from 'typescript';
22

3-
export enum DynamodbPrimitiveTypes {
3+
export class DynamodbPrimitiveType {
4+
constructor(public readonly kind: DynamodbPrimitiveTypeKinds, private readonly flag: number) {}
5+
6+
public isBigInt() {
7+
return (
8+
this.kind === DynamodbPrimitiveTypeKinds.Number &&
9+
(this.flag & DynamodbPrimitiveTypeFlags.BigInt) === DynamodbPrimitiveTypeFlags.BigInt
10+
);
11+
}
12+
}
13+
14+
export enum DynamodbPrimitiveTypeKinds {
415
String = 'S',
516
Number = 'N',
617
Boolean = 'BOOL',
718
Null = 'NULL',
819
Binary = 'B',
920
}
1021

11-
export function dynamodbPrimitiveTypeFromTypeFlag(flag: ts.TypeFlags | undefined): DynamodbPrimitiveTypes | undefined {
22+
export enum DynamodbPrimitiveTypeFlags {
23+
NA = 0b0000,
24+
BigInt = 0b0001,
25+
}
26+
27+
export function dynamodbPrimitiveTypeFromTypeFlag(flag: ts.TypeFlags | undefined): DynamodbPrimitiveType | undefined {
1228
if (flag === undefined) {
1329
return undefined;
1430
}
1531
if ((flag & ts.TypeFlags.String) === ts.TypeFlags.String) {
16-
return DynamodbPrimitiveTypes.String;
32+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.String, DynamodbPrimitiveTypeFlags.NA);
1733
}
1834
if ((flag & ts.TypeFlags.Number) === ts.TypeFlags.Number) {
19-
return DynamodbPrimitiveTypes.Number;
35+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Number, DynamodbPrimitiveTypeFlags.NA);
2036
}
2137
if ((flag & ts.TypeFlags.Boolean) === ts.TypeFlags.Boolean) {
22-
return DynamodbPrimitiveTypes.Boolean;
38+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Boolean, DynamodbPrimitiveTypeFlags.NA);
2339
}
2440
if ((flag & ts.TypeFlags.Unknown) === ts.TypeFlags.Unknown) {
25-
return DynamodbPrimitiveTypes.Null;
41+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Null, DynamodbPrimitiveTypeFlags.NA);
2642
}
2743
return undefined;
2844
}
2945

30-
export function dynamodbPrimitiveTypeFromName(typeName: string | undefined): DynamodbPrimitiveTypes | undefined {
46+
export function dynamodbPrimitiveTypeFromName(typeName: string | undefined): DynamodbPrimitiveType | undefined {
3147
if (typeName === undefined) {
3248
return undefined;
3349
}
@@ -39,16 +55,17 @@ export function dynamodbPrimitiveTypeFromName(typeName: string | undefined): Dyn
3955

4056
switch (typeName) {
4157
case 'string':
42-
return DynamodbPrimitiveTypes.String;
58+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.String, DynamodbPrimitiveTypeFlags.NA);
4359
case 'number':
60+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Number, DynamodbPrimitiveTypeFlags.NA);
4461
case 'BigInt':
45-
return DynamodbPrimitiveTypes.Number;
62+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Number, DynamodbPrimitiveTypeFlags.BigInt);
4663
case 'boolean':
47-
return DynamodbPrimitiveTypes.Boolean;
64+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Boolean, DynamodbPrimitiveTypeFlags.NA);
4865
case 'unknown':
49-
return DynamodbPrimitiveTypes.Null;
66+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Null, DynamodbPrimitiveTypeFlags.NA);
5067
case 'Uint8Array':
51-
return DynamodbPrimitiveTypes.Binary;
68+
return new DynamodbPrimitiveType(DynamodbPrimitiveTypeKinds.Binary, DynamodbPrimitiveTypeFlags.NA);
5269
default:
5370
return undefined;
5471
}
@@ -60,22 +77,24 @@ export interface DynamodbPrimitiveValueToJSValueConvertingOp {
6077
}
6178

6279
export function dynamodbPrimitiveValueToJSValueConvertingOp(
63-
primitiveType: DynamodbPrimitiveTypes,
80+
primitiveType: DynamodbPrimitiveType,
6481
): DynamodbPrimitiveValueToJSValueConvertingOp {
65-
switch (primitiveType) {
66-
case DynamodbPrimitiveTypes.Number:
82+
switch (primitiveType.kind) {
83+
case DynamodbPrimitiveTypeKinds.Number:
6784
return {
6885
leftOp: '(() => { const numStr = ',
69-
rightOp: '; return numStr === undefined ? undefined : Number(numStr); })()',
86+
rightOp: `; return numStr === undefined ? undefined : ${
87+
primitiveType.isBigInt() ? 'BigInt' : 'Number'
88+
}(numStr); })()`,
7089
};
71-
case DynamodbPrimitiveTypes.Null:
90+
case DynamodbPrimitiveTypeKinds.Null:
7291
return {
7392
leftOp: '(',
7493
rightOp: ' === true ? null : undefined)',
7594
};
76-
case DynamodbPrimitiveTypes.String:
77-
case DynamodbPrimitiveTypes.Binary:
78-
case DynamodbPrimitiveTypes.Boolean:
95+
case DynamodbPrimitiveTypeKinds.String:
96+
case DynamodbPrimitiveTypeKinds.Binary:
97+
case DynamodbPrimitiveTypeKinds.Boolean:
7998
default:
8099
return {
81100
leftOp: '',

0 commit comments

Comments
 (0)