-
Notifications
You must be signed in to change notification settings - Fork 127
/
reflection-info.ts
426 lines (362 loc) · 11.2 KB
/
reflection-info.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
import {lowerCamelCase} from "./lower-camel-case";
import {IMessageType} from "./message-type-contract";
import {JsonValue} from "./json-typings";
/**
* Describes a protobuf enum for runtime reflection.
*
* The tuple consists of:
*
*
* [0] the protobuf type name
*
* The type name follows the same rules as message type names.
* See `MessageInfo` for details.
*
*
* [1] the enum object generated by Typescript
*
* We generate standard Typescript enums for protobuf enums. They are compiled
* to lookup objects that map from numerical value to name strings and vice
* versa and can also contain alias names.
*
* See https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
*
* We use this lookup feature to when encoding / decoding JSON format. The
* enum is guaranteed to have a value for 0. We generate an entry for 0 if
* none was declared in .proto because we would need to support custom default
* values if we didn't.
*
*
* [2] the prefix shared by all original enum values (optional)
*
* If all values of a protobuf enum share a prefix, it is dropped in the
* generated enum. For example, the protobuf enum `enum My { MY_FOO, MY_BAR }`
* becomes the typescript enum `enum My { FOO, BAR }`.
*
* Because the JSON format requires the original value name, we store the
* dropped prefix here, so that the JSON format implementation can restore
* the original value names.
*/
export type EnumInfo = readonly [
/**
* The protobuf type name of the enum
*/
string,
/**
* The enum object generated by Typescript
*/
{
[key: number]: string;
[k: string]: number | string;
},
/**
* The prefix shared by all original enum values
*/
string?
];
/**
* Describes a protobuf message for runtime reflection.
*/
export interface MessageInfo {
/**
* The protobuf type name of the message, including package and
* parent types if present.
*
* If the .proto file included a `package` statement, the type name
* starts with '.'.
*
* Examples:
* 'MyNamespaceLessMessage'
* '.my_package.MyMessage'
* '.my_package.ParentMessage.ChildMessage'
*/
readonly typeName: string;
/**
* Simple information for each message field, in the order
* of declaration in the source .proto.
*/
readonly fields: readonly FieldInfo[];
}
/**
* Describes a field of a protobuf message for runtime
* reflection. We distinguish between the following
* kinds of fields:
*
* "scalar": string, bool, float, int32, etc.
* See https://developers.google.com/protocol-buffers/docs/proto3#scalar
*
* "enum": field was declared with an enum type.
*
* "message": field was declared with a message type.
*
* "map": field was declared with map<K,V>.
*
*
* Every field, regardless of it's kind, always has the following properties:
*
* "no": The field number of the .proto field.
* "name": The original name of the .proto field.
* "localName": The name of the field as used in generated code.
* "jsonName": The name for JSON serialization / deserialization.
* "options": Custom field options from the .proto source in JSON format.
*
*
* Other properties:
*
* - Fields of kind "scalar", "enum" and "message" can have a "repeat" type.
* - Fields of kind "scalar" and "enum" can have a "repeat" type.
* - Fields of kind "scalar", "enum" and "message" can be member of a "oneof".
*
* A field can be only have one of the above properties set.
*
* Options for "scalar" fields:
*
* - 64 bit integral types can provide "L" - the JavaScript representation
* type.
*
*/
export type FieldInfo = fiRules<fiScalar> | fiRules<fiEnum> | fiRules<fiMessage> | fiRules<fiMap>;
/**
* Version of `FieldInfo` that allows the following properties
* to be omitted:
* - "localName", "jsonName": can be omitted if equal to lowerCamelCase(name)
* - "opt": can be omitted if false
* - "repeat", can be omitted if RepeatType.NO
*
* Use `normalizeFieldInfo()` to fill the omitted fields with
* their standard values.
*/
export type PartialFieldInfo =
| fiPartialRules<fiScalar>
| fiPartialRules<fiEnum>
| fiPartialRules<fiMessage>
| fiPartialRules<fiMap>;
interface fiShared {
/**
* The field number of the .proto field.
*/
no: number;
/**
* The original name of the .proto field.
*/
name: string;
/**
* The name of the field as used in generated code.
*/
localName: string;
/**
* The name for JSON serialization / deserialization.
*/
jsonName: string;
/**
* The name of the `oneof` group, if this field belongs to one.
*/
oneof: string | undefined;
/**
* Contains custom field options from the .proto source.
*/
options?: { [extensionName: string]: JsonValue };
}
interface fiScalar extends fiShared {
kind: 'scalar';
/**
* Scalar type of the field.
*/
T: ScalarType;
/**
* Representation of 64 bit integral types (int64, uint64, sint64,
* fixed64, sfixed64).
*
* If this option is set for other scalar types, it is ignored.
* Omitting this option is equivalent to `STRING`.
*/
L?: LongType;
/**
* Is the field repeated?
*/
repeat: RepeatType;
/**
* Is the field optional?
*/
opt: boolean;
}
interface fiMessage extends fiShared {
kind: 'message';
/**
* Message handler for the field.
*/
T: () => IMessageType<any>;
/**
* Is the field repeated?
*/
repeat: RepeatType;
}
interface fiEnum extends fiShared {
kind: 'enum';
/**
* Enum type information for the field.
*/
T: () => EnumInfo;
/**
* Is the field repeated?
*/
repeat: RepeatType;
/**
* Is the field optional?
*/
opt: boolean;
}
interface fiMap extends fiShared {
kind: 'map';
/**
* Map key type.
*
* The key_type can be any integral or string type
* (so, any scalar type except for floating point
* types and bytes)
*/
K: Exclude<ScalarType, ScalarType.FLOAT | ScalarType.DOUBLE | ScalarType.BYTES>;
/**
* Map value type. Can be a `ScalarType`, enum type information,
* or type handler for a message.
*/
V:
| { kind: 'scalar', T: ScalarType, L?: LongType }
| { kind: 'enum', T: () => EnumInfo }
| { kind: 'message', T: () => IMessageType<any> };
}
type fiRules<T> = Omit<T, 'oneof' | 'repeat' | 'opt'> & (
| { repeat: RepeatType.NO, opt: false; oneof: undefined; }
| { repeat: RepeatType.NO, opt: true; oneof: undefined; }
| { repeat: RepeatType.PACKED | RepeatType.UNPACKED, opt: false; oneof: undefined; }
| { repeat: RepeatType.NO, opt: false; oneof: string; });
type fiPartialRules<T> = Omit<T, 'jsonName' | 'localName' | 'oneof' | 'repeat' | 'opt'> & (
| { localName?: string, jsonName?: string, repeat?: RepeatType.NO, opt?: false; oneof?: undefined; }
| { localName?: string, jsonName?: string, repeat?: RepeatType.NO, opt: true; oneof?: undefined; }
| { localName?: string, jsonName?: string, repeat: RepeatType.PACKED | RepeatType.UNPACKED, opt?: false; oneof?: undefined; }
| { localName?: string, jsonName?: string, repeat?: RepeatType.NO, opt?: false; oneof: string; });
/**
* Scalar value types. This is a subset of field types declared by protobuf
* enum google.protobuf.FieldDescriptorProto.Type The types GROUP and MESSAGE
* are omitted, but the numerical values are identical.
*/
export enum ScalarType {
// 0 is reserved for errors.
// Order is weird for historical reasons.
DOUBLE = 1,
FLOAT = 2,
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
INT64 = 3,
UINT64 = 4,
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
INT32 = 5,
FIXED64 = 6,
FIXED32 = 7,
BOOL = 8,
STRING = 9,
// Tag-delimited aggregate.
// Group type is deprecated and not supported in proto3. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields.
// TYPE_GROUP = 10,
// TYPE_MESSAGE = 11, // Length-delimited aggregate.
// New in version 2.
BYTES = 12,
UINT32 = 13,
// TYPE_ENUM = 14,
SFIXED32 = 15,
SFIXED64 = 16,
SINT32 = 17, // Uses ZigZag encoding.
SINT64 = 18, // Uses ZigZag encoding.
}
/**
* JavaScript representation of 64 bit integral types. Equivalent to the
* field option "jstype".
*
* By default, protobuf-ts represents 64 bit types as `bigint`.
*
* You can change the default behaviour by enabling the plugin parameter
* `long_type_string`, which will represent 64 bit types as `string`.
*
* Alternatively, you can change the behaviour for individual fields
* with the field option "jstype":
*
* ```protobuf
* uint64 my_field = 1 [jstype = JS_STRING];
* uint64 other_field = 2 [jstype = JS_NUMBER];
* ```
*/
export enum LongType {
/**
* Use JavaScript `bigint`.
*
* Field option `[jstype = JS_NORMAL]`.
*/
BIGINT = 0,
/**
* Use JavaScript `string`.
*
* Field option `[jstype = JS_STRING]`.
*/
STRING = 1,
/**
* Use JavaScript `number`.
*
* Large values will loose precision.
*
* Field option `[jstype = JS_NUMBER]`.
*/
NUMBER = 2,
}
/**
* Protobuf 2.1.0 introduced packed repeated fields.
* Setting the field option `[packed = true]` enables packing.
*
* In proto3, all repeated fields are packed by default.
* Setting the field option `[packed = false]` disables packing.
*
* Packed repeated fields are encoded with a single tag,
* then a length-delimiter, then the element values.
*
* Unpacked repeated fields are encoded with a tag and
* value for each element.
*
* `bytes` and `string` cannot be packed.
*/
export enum RepeatType {
/**
* The field is not repeated.
*/
NO = 0,
/**
* The field is repeated and should be packed.
* Invalid for `bytes` and `string`, they cannot be packed.
*/
PACKED = 1,
/**
* The field is repeated but should not be packed.
* The only valid repeat type for repeated `bytes` and `string`.
*/
UNPACKED = 2,
}
/**
* Makes FieldInfo from partial information.
*/
export function normalizeFieldInfo(field: PartialFieldInfo): FieldInfo {
field.localName = field.localName ?? lowerCamelCase(field.name);
field.jsonName = field.jsonName ?? lowerCamelCase(field.name);
field.repeat = field.repeat ?? RepeatType.NO;
field.opt = field.opt ?? (field.repeat ? false : field.oneof ? false : field.kind == "message");
return field as FieldInfo;
}
/**
* Read custom field options from a generated message type.
*/
export function readFieldOptions<T extends object>(messageType: MessageInfo | IMessageType<any>, fieldName: string | number, extensionName: string, extensionType: IMessageType<T>): T | undefined {
let info = messageType.fields.find((m, i) => m.localName == fieldName || i == fieldName);
return info && info.options && info.options[extensionName]
? extensionType.fromJson(info.options[extensionName])
: undefined;
}