From d3ea8eb69e19a5e45fcc1766c4af1194b17e48fc Mon Sep 17 00:00:00 2001 From: Christian Schroeder Date: Sun, 21 Nov 2021 16:38:06 +0100 Subject: [PATCH] fix: code generation for int64 map values in fromPartial and fromJson (#395) * fix code generation for int64 map values in fromPartial and fromJson * Add tests and some follow up fixes. * Fix test. Co-authored-by: christians Co-authored-by: Stephen Haberman --- .../simple-long/google/protobuf/wrappers.bin | Bin 4316 -> 4316 bytes .../simple-long/google/protobuf/wrappers.ts | 16 +- integration/simple-long/simple-test.ts | 76 ++++++++++ integration/simple-long/simple.bin | Bin 6685 -> 6984 bytes integration/simple-long/simple.proto | 2 + integration/simple-long/simple.ts | 138 ++++++++++++++---- integration/simple/simple-json-test.ts | 5 + integration/simple/simple-test.ts | 22 ++- integration/simple/simple.bin | Bin 21541 -> 21734 bytes integration/simple/simple.proto | 1 + integration/simple/simple.ts | 92 ++++++++++++ src/main.ts | 28 ++-- 12 files changed, 319 insertions(+), 61 deletions(-) create mode 100644 integration/simple-long/simple-test.ts diff --git a/integration/simple-long/google/protobuf/wrappers.bin b/integration/simple-long/google/protobuf/wrappers.bin index ba0182a949a0445f1e7e064194b0a8fe2a7ed22a..136184cf883f0b5338c4486eb7ee3d30735c2c52 100644 GIT binary patch delta 21 ccmcbkct>%9DhICwgAzm4dc_F}8-4o)07{t#mH+?% delta 21 ccmcbkct>%9DhH1QixNZCdc_F}8-4o)07|): Int64Value { const message = { ...baseInt64Value } as Int64Value; - if (object.value !== undefined && object.value !== null) { - message.value = object.value as Long; - } else { - message.value = Long.ZERO; - } + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.ZERO; return message; }, }; @@ -281,11 +277,7 @@ export const UInt64Value = { fromPartial(object: DeepPartial): UInt64Value { const message = { ...baseUInt64Value } as UInt64Value; - if (object.value !== undefined && object.value !== null) { - message.value = object.value as Long; - } else { - message.value = Long.UZERO; - } + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.UZERO; return message; }, }; @@ -560,9 +552,11 @@ function base64FromBytes(arr: Uint8Array): string { return btoa(bin.join('')); } -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined | Long; +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T + : T extends Long + ? string | number | Long : T extends Array ? Array> : T extends ReadonlyArray diff --git a/integration/simple-long/simple-test.ts b/integration/simple-long/simple-test.ts new file mode 100644 index 000000000..79f942076 --- /dev/null +++ b/integration/simple-long/simple-test.ts @@ -0,0 +1,76 @@ +import { SimpleWithMap } from './simple'; + +describe('simple', () => { + it('can fromPartial maps', () => { + const s1 = SimpleWithMap.fromPartial({ + intLookup: { 1: 2, 2: 1 }, + longLookup: { '1': 2, '2': 1 }, + }); + expect(s1).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": Long { + "high": 0, + "low": 2, + "unsigned": false, + }, + "2": Long { + "high": 0, + "low": 1, + "unsigned": false, + }, + }, + "nameLookup": Object {}, + } + `); + }); + + it('can toJSON/fromJSON maps', () => { + const s1 = SimpleWithMap.fromPartial({ + intLookup: { 1: 2, 2: 1 }, + longLookup: { '1': 2, '2': 1 }, + }); + + const json = SimpleWithMap.toJSON(s1); + expect(json).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": "2", + "2": "1", + }, + "nameLookup": Object {}, + } + `); + + const s2 = SimpleWithMap.fromJSON(JSON.parse(JSON.stringify(json))); + expect(s2).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": Long { + "high": 0, + "low": 2, + "unsigned": false, + }, + "2": Long { + "high": 0, + "low": 1, + "unsigned": false, + }, + }, + "nameLookup": Object {}, + } + `); + }); +}); diff --git a/integration/simple-long/simple.bin b/integration/simple-long/simple.bin index 427793dea5edcd17bbe79aefe3d96afc24570b07..b1ad8ee48a7a98359687c24a71adbffa1a26902a 100644 GIT binary patch delta 1094 zcmX}sKX21O7zXh8&PnWZs-{=RNt}O&3!)OJm7o*}0TUfSoq!oNX{@$1wXN6@iiG3= z@d>gab!SBtGikP|4}xUAe3|#H ze#;Mr`$Vi~)zu$)N8?vVn{PDpuO(_yuT-|Ke%PNiO|By6eYzC7t}!P02D|Cb)bpu7 zp3Ro(oqv0(5MEN1u^;3Ou^Pwf#VZv~-ulN0QZQ9V z+44u~cpL;_q9(pt#Qrqv8>@4F8Sevd_nM_xyb9-%!`1F=yixPjd&?tv-!khP1F2{t5Vf(?n8U_;v}n2>zLx&;$p z>4;rFh>Quik+oSC;YLoUaF*nXoCT+dSzwBo1*X8B0+{T9A50MzIV4Dcr>hm(VkUqxq;E1>=#$mz-VuDV^YiiG4B5dlPP{B delta 834 zcmX}ry>8S%5C`z{%=&9>6leVH^BukyNE1Rs8>LGHPY{U$36uyZL9Td$E0B=rX`>VG zKocoV9wbd_{&v<={hsXS_7e81Vh*_<6hs*Mh=Vkk+>f2~784@F_ooB6OL!PiiM>es> z5UqrBDr{=~Dfm;0mW0AQK3WnZN$wby3KF;EffW=kX+1xLUh2aJ6=a!bX`tr{z+7utu2=)+h^v zTV(-Qi`~ZOs}0~apoG6d_Cg`fxPRhPhR w)g^FSbqU-S?tFfjsqnDB>n_d)El+`mWbO*5OMwUJ-KHt<@N#z*%)jOIKdUb|MF0Q* diff --git a/integration/simple-long/simple.proto b/integration/simple-long/simple.proto index c7bbc8419..9c812fc70 100644 --- a/integration/simple-long/simple.proto +++ b/integration/simple-long/simple.proto @@ -14,6 +14,8 @@ message SimpleWithWrappers { message SimpleWithMap { map nameLookup = 2; map intLookup = 3; + // Ideally we'd test map but we present maps as JS objects and `Long` cannot be used as a keys. + map longLookup = 4; } message Numbers { diff --git a/integration/simple-long/simple.ts b/integration/simple-long/simple.ts index bc06e402b..55dfae0da 100644 --- a/integration/simple-long/simple.ts +++ b/integration/simple-long/simple.ts @@ -17,6 +17,8 @@ export interface SimpleWithWrappers { export interface SimpleWithMap { nameLookup: { [key: string]: string }; intLookup: { [key: number]: number }; + /** Ideally we'd test map but we present maps as JS objects and `Long` cannot be used as a keys. */ + longLookup: { [key: string]: Long }; } export interface SimpleWithMap_NameLookupEntry { @@ -29,6 +31,11 @@ export interface SimpleWithMap_IntLookupEntry { value: number; } +export interface SimpleWithMap_LongLookupEntry { + key: string; + value: Long; +} + export interface Numbers { double: number; float: number; @@ -142,11 +149,8 @@ export const SimpleWithWrappers = { message.name = object.name ?? undefined; message.age = object.age ?? undefined; message.enabled = object.enabled ?? undefined; - if (object.bananas !== undefined && object.bananas !== null) { - message.bananas = object.bananas as Long | undefined; - } else { - message.bananas = undefined; - } + message.bananas = + object.bananas !== undefined && object.bananas !== null ? Long.fromValue(object.bananas) : undefined; message.coins = (object.coins ?? []).map((e) => e); message.snacks = (object.snacks ?? []).map((e) => e); return message; @@ -163,6 +167,9 @@ export const SimpleWithMap = { Object.entries(message.intLookup).forEach(([key, value]) => { SimpleWithMap_IntLookupEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); }); + Object.entries(message.longLookup).forEach(([key, value]) => { + SimpleWithMap_LongLookupEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).ldelim(); + }); return writer; }, @@ -172,6 +179,7 @@ export const SimpleWithMap = { const message = { ...baseSimpleWithMap } as SimpleWithMap; message.nameLookup = {}; message.intLookup = {}; + message.longLookup = {}; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -187,6 +195,12 @@ export const SimpleWithMap = { message.intLookup[entry3.key] = entry3.value; } break; + case 4: + const entry4 = SimpleWithMap_LongLookupEntry.decode(reader, reader.uint32()); + if (entry4.value !== undefined) { + message.longLookup[entry4.key] = entry4.value; + } + break; default: reader.skipType(tag & 7); break; @@ -209,6 +223,12 @@ export const SimpleWithMap = { message.intLookup[Number(key)] = Number(value); }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + message.longLookup[key] = Long.fromString(value as string); + }); + } return message; }, @@ -226,6 +246,12 @@ export const SimpleWithMap = { obj.intLookup[k] = v; }); } + obj.longLookup = {}; + if (message.longLookup) { + Object.entries(message.longLookup).forEach(([k, v]) => { + obj.longLookup[k] = v.toString(); + }); + } return obj; }, @@ -247,6 +273,14 @@ export const SimpleWithMap = { } }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + if (value !== undefined) { + message.longLookup[key] = Long.fromValue(value); + } + }); + } return message; }, }; @@ -363,6 +397,62 @@ export const SimpleWithMap_IntLookupEntry = { }, }; +const baseSimpleWithMap_LongLookupEntry: object = { key: '', value: Long.ZERO }; + +export const SimpleWithMap_LongLookupEntry = { + encode(message: SimpleWithMap_LongLookupEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== '') { + writer.uint32(10).string(message.key); + } + if (!message.value.isZero()) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SimpleWithMap_LongLookupEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.string(); + break; + case 2: + message.value = reader.int64() as Long; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key !== undefined && object.key !== null ? String(object.key) : ''; + message.value = object.value !== undefined && object.value !== null ? Long.fromString(object.value) : Long.ZERO; + return message; + }, + + toJSON(message: SimpleWithMap_LongLookupEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = (message.value || Long.ZERO).toString()); + return obj; + }, + + fromPartial(object: DeepPartial): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key ?? ''; + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.ZERO; + return message; + }, +}; + const baseNumbers: object = { double: 0, float: 0, @@ -535,43 +625,27 @@ export const Numbers = { message.double = object.double ?? 0; message.float = object.float ?? 0; message.int32 = object.int32 ?? 0; - if (object.int64 !== undefined && object.int64 !== null) { - message.int64 = object.int64 as Long; - } else { - message.int64 = Long.ZERO; - } + message.int64 = object.int64 !== undefined && object.int64 !== null ? Long.fromValue(object.int64) : Long.ZERO; message.uint32 = object.uint32 ?? 0; - if (object.uint64 !== undefined && object.uint64 !== null) { - message.uint64 = object.uint64 as Long; - } else { - message.uint64 = Long.UZERO; - } + message.uint64 = object.uint64 !== undefined && object.uint64 !== null ? Long.fromValue(object.uint64) : Long.UZERO; message.sint32 = object.sint32 ?? 0; - if (object.sint64 !== undefined && object.sint64 !== null) { - message.sint64 = object.sint64 as Long; - } else { - message.sint64 = Long.ZERO; - } + message.sint64 = object.sint64 !== undefined && object.sint64 !== null ? Long.fromValue(object.sint64) : Long.ZERO; message.fixed32 = object.fixed32 ?? 0; - if (object.fixed64 !== undefined && object.fixed64 !== null) { - message.fixed64 = object.fixed64 as Long; - } else { - message.fixed64 = Long.UZERO; - } + message.fixed64 = + object.fixed64 !== undefined && object.fixed64 !== null ? Long.fromValue(object.fixed64) : Long.UZERO; message.sfixed32 = object.sfixed32 ?? 0; - if (object.sfixed64 !== undefined && object.sfixed64 !== null) { - message.sfixed64 = object.sfixed64 as Long; - } else { - message.sfixed64 = Long.ZERO; - } - message.manyUint64 = (object.manyUint64 ?? []).map((e) => e); + message.sfixed64 = + object.sfixed64 !== undefined && object.sfixed64 !== null ? Long.fromValue(object.sfixed64) : Long.ZERO; + message.manyUint64 = (object.manyUint64 ?? []).map((e) => Long.fromValue(e)); return message; }, }; -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined | Long; +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T + : T extends Long + ? string | number | Long : T extends Array ? Array> : T extends ReadonlyArray diff --git a/integration/simple/simple-json-test.ts b/integration/simple/simple-json-test.ts index 7891a5f5d..cf3d6059f 100644 --- a/integration/simple/simple-json-test.ts +++ b/integration/simple/simple-json-test.ts @@ -190,6 +190,7 @@ describe('simple json', () => { "intLookup": Object { "1": 0, }, + "longLookup": Object {}, "mapOfBytes": Object {}, "mapOfStringValues": Object {}, "mapOfTimestamps": Object {}, @@ -212,6 +213,7 @@ describe('simple json', () => { Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object {}, "mapOfStringValues": Object {}, "mapOfTimestamps": Object { @@ -236,12 +238,14 @@ describe('simple json', () => { b: new Uint8Array([1, 2, 3]), }, mapOfStringValues: {}, + longLookup: {}, }; const json = SimpleWithMap.toJSON(s1); expect(json).toMatchInlineSnapshot(` Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object { "a": "AQI=", "b": "AQID", @@ -265,6 +269,7 @@ describe('simple json', () => { Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object { "a": Uint8Array [ 1, diff --git a/integration/simple/simple-test.ts b/integration/simple/simple-test.ts index f6d6c79a9..f87be3048 100644 --- a/integration/simple/simple-test.ts +++ b/integration/simple/simple-test.ts @@ -232,8 +232,10 @@ describe('simple', () => { mapOfTimestamps: {}, mapOfBytes: {}, mapOfStringValues: { a: '1', b: '', c: undefined }, + longLookup: { 1: 2, 2: 1 }, }; - const s2 = PbSimpleWithMap.toObject(PbSimpleWithMap.decode(SimpleWithMap.encode(s1).finish())); + // const s2 = PbSimpleWithMap.toObject(PbSimpleWithMap.decode(SimpleWithMap.encode(s1).finish())); + const s2 = SimpleWithMap.decode(SimpleWithMap.encode(s1).finish()); expect(s2).toMatchInlineSnapshot(` Object { "entitiesById": Object { @@ -248,12 +250,16 @@ describe('simple', () => { "1": 2, "2": 1, }, + "longLookup": Object { + "1": 2, + "2": 1, + }, + "mapOfBytes": Object {}, "mapOfStringValues": Object { - "a": Object { - "value": "1", - }, - "b": Object {}, + "a": "1", + "b": "", }, + "mapOfTimestamps": Object {}, "nameLookup": Object { "foo": "bar", }, @@ -269,6 +275,7 @@ describe('simple', () => { mapOfTimestamps: {}, mapOfBytes: {}, mapOfStringValues: { foo: '', bar: undefined }, + longLookup: { 1: 2, 2: 1 }, }; const s2 = SimpleWithMap.decode(SimpleWithMap.encode(s1).finish()); expect(s2).toEqual(s1); @@ -317,6 +324,7 @@ describe('simple', () => { const s1 = SimpleWithMap.fromPartial({ intLookup: { 1: 2, 2: 0 }, mapOfStringValues: { a: '1', b: '', c: undefined }, + longLookup: { 1: 2, 2: 0 }, }); expect(s1).toMatchInlineSnapshot(` Object { @@ -325,6 +333,10 @@ describe('simple', () => { "1": 2, "2": 0, }, + "longLookup": Object { + "1": 2, + "2": 0, + }, "mapOfBytes": Object {}, "mapOfStringValues": Object { "a": "1", diff --git a/integration/simple/simple.bin b/integration/simple/simple.bin index 8ebaa258b441049a30f6d802e9cde9339055b6ca..eb98fb18e55275c972fcf16250eeb43bcf19851d 100644 GIT binary patch delta 1950 zcmZ9NU2hvj6o&WA&aQW7Y+_HG#BtNq3H=C2B?>`;3Mzq6xuL2=-~x%OLLIlYYez|% z(0naPnjn>^X|zH41xRp(kU_cVf2hPi;EHR00`JVutPyv4e9rsKoXL#+)6ewxFEk;F zD-Rwmb((K2E%#P?^(gYorJ6VGZS0ms`f6_MxB0y}_UkA8^SM6)b{_S+@BT$)bxVj& zuiLua>$M**)e7m$_a-jf&Of--U0r^%@Sl$Uog07BJN27l=zmSBEc|x!NsSsb<1gqx zRaFFQcRP=pHKsr7SKpUGd_E%zDq!AOf9As#j><`ZaJi>hW^p?&N2zyR>ATOSCG>+z8Bf#6$LdMw7J3O2m&Df3CR zZA3%HC$(?W`T+24TAyTz7H(Q0iV$uZA?K2zw8fNJ3xM1fD>%}%sN@K-t#ZXKhZEe+ zV+^tFJf@Y~d5rlg@wf9FBs+P|M{Xz2`N-|$IbV0?F|Pb1pN=DzcF5XGa}8$=5U zvS63RGXmzz7LeT=Wbr`uiH+K2BSe9*LqzvUR2(9@Pt_4SEiUg^D zI)n3UjOPMhY3ODtD*tpFKWP!pVq6Z|09U^)_qct0qiXRT55>cRw;*#=9hl8v{ z_E2YU;yM>q_>8)J!pK?YBtO;MMe6*WQqR zB^9w?xBKl=e0Gr_|`m!NAV88Ny<>t?ME0NrfPhKvD-&e?k51Hm_2_~JnExnRNg zV%;^D^lM)nXx}`W&;ku8&BwwK1Ia;0(9It>X||Ut{v>!>Y%k3bdQ$j!s(p*B-)smF zX-XWSaWUQF2)@OtGCraB^Wa6{6X<%L)Xca->z3?!0iav5IuII{(ms*vqXojZr1V&f zOBKBg-cliy=zE#;8KKm|mK6dZY*`@)jV%bZ4+LM!_&5lqWu|P25L;#?OF8LcQg%h; zmMc}e9FB0s=NNJ;KBt{4KE@DR@iD-vkA;Y>`dEnAs*i=C%EMKk<0@S9({be1{B#_- zH9sB43Tu8kH0C}Q87e-SWD#Pyk41>(h-J9ilEsR@B9_-g*_>D$8BQFMMnr8j)abK# zFzbXhi(&~;po`)K0e!TD=z1@T$FV_d&_A3{t8O?v4j_iZ2fU6`Lw1|keyidIIu(a%o1_~9&jmQpaLrI{ z)4owV6;5EMv-j3SzSG-F1i=o;x=rBW0Eb5%La;*z^pCUtfavWIJ7J44wgPlpiA`0y qnPZqbG~$jchN(m2$MrAgK3-OQmx2m@ZbDv0<1QuTyRrEXH}xOcc%ke7 diff --git a/integration/simple/simple.proto b/integration/simple/simple.proto index a7c6dad79..98ad06038 100644 --- a/integration/simple/simple.proto +++ b/integration/simple/simple.proto @@ -97,6 +97,7 @@ message SimpleWithMap { map mapOfTimestamps = 4; map mapOfBytes = 5; map mapOfStringValues = 6; + map longLookup = 7; } message SimpleWithSnakeCaseMap { diff --git a/integration/simple/simple.ts b/integration/simple/simple.ts index 7a35e3d16..f2329834f 100644 --- a/integration/simple/simple.ts +++ b/integration/simple/simple.ts @@ -194,6 +194,7 @@ export interface SimpleWithMap { mapOfTimestamps: { [key: string]: Date }; mapOfBytes: { [key: string]: Uint8Array }; mapOfStringValues: { [key: string]: string | undefined }; + longLookup: { [key: number]: number }; } export interface SimpleWithMap_EntitiesByIdEntry { @@ -226,6 +227,11 @@ export interface SimpleWithMap_MapOfStringValuesEntry { value: string | undefined; } +export interface SimpleWithMap_LongLookupEntry { + key: number; + value: number; +} + export interface SimpleWithSnakeCaseMap { entitiesById: { [key: number]: Entity }; } @@ -957,6 +963,9 @@ export const SimpleWithMap = { SimpleWithMap_MapOfStringValuesEntry.encode({ key: key as any, value }, writer.uint32(50).fork()).ldelim(); } }); + Object.entries(message.longLookup).forEach(([key, value]) => { + SimpleWithMap_LongLookupEntry.encode({ key: key as any, value }, writer.uint32(58).fork()).ldelim(); + }); return writer; }, @@ -970,6 +979,7 @@ export const SimpleWithMap = { message.mapOfTimestamps = {}; message.mapOfBytes = {}; message.mapOfStringValues = {}; + message.longLookup = {}; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -1009,6 +1019,12 @@ export const SimpleWithMap = { message.mapOfStringValues[entry6.key] = entry6.value; } break; + case 7: + const entry7 = SimpleWithMap_LongLookupEntry.decode(reader, reader.uint32()); + if (entry7.value !== undefined) { + message.longLookup[entry7.key] = entry7.value; + } + break; default: reader.skipType(tag & 7); break; @@ -1055,6 +1071,12 @@ export const SimpleWithMap = { message.mapOfStringValues[key] = value as string | undefined; }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + message.longLookup[Number(key)] = Number(value); + }); + } return message; }, @@ -1096,6 +1118,12 @@ export const SimpleWithMap = { obj.mapOfStringValues[k] = v; }); } + obj.longLookup = {}; + if (message.longLookup) { + Object.entries(message.longLookup).forEach(([k, v]) => { + obj.longLookup[k] = v; + }); + } return obj; }, @@ -1149,6 +1177,14 @@ export const SimpleWithMap = { } }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + if (value !== undefined) { + message.longLookup[Number(key)] = Number(value); + } + }); + } return message; }, }; @@ -1492,6 +1528,62 @@ export const SimpleWithMap_MapOfStringValuesEntry = { }, }; +const baseSimpleWithMap_LongLookupEntry: object = { key: 0, value: 0 }; + +export const SimpleWithMap_LongLookupEntry = { + encode(message: SimpleWithMap_LongLookupEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== 0) { + writer.uint32(8).int64(message.key); + } + if (message.value !== 0) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SimpleWithMap_LongLookupEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = longToNumber(reader.int64() as Long); + break; + case 2: + message.value = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key !== undefined && object.key !== null ? Number(object.key) : 0; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: SimpleWithMap_LongLookupEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key ?? 0; + message.value = object.value ?? 0; + return message; + }, +}; + const baseSimpleWithSnakeCaseMap: object = {}; export const SimpleWithSnakeCaseMap = { diff --git a/src/main.ts b/src/main.ts index ea3744a18..276217339 100644 --- a/src/main.ts +++ b/src/main.ts @@ -399,16 +399,19 @@ function makeDeepPartial(options: Options, longs: ReturnType` : code`keyof T`; // Based on the type from ts-essentials const DeepPartial = conditionalOutput( 'DeepPartial', code` - type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined${maybeLong}; - ${maybeExport} type DeepPartial = T extends Builtin + type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + ${maybeExport} type DeepPartial = T extends Builtin ? T + ${maybeLong} : T extends Array ? Array> : T extends ReadonlyArray @@ -952,6 +955,8 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto } else { return code`${utils.bytesFromBase64}(${from} as string)`; } + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`Long.fromString(${from} as string)`; } else if (isEnum(valueType)) { return code`${from} as number`; } else { @@ -1061,6 +1066,8 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP return code`${from}`; } else if (isTimestamp(valueType) && options.useDate === DateOption.TIMESTAMP) { return code`${utils.fromTimestamp}(${from}).toISOString()`; + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`${from}.toString()`; } else if (isScalar(valueType) || isValueType(ctx, valueType)) { return code`${from}`; } else { @@ -1134,7 +1141,9 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const fieldName = maybeSnakeToCamel(field.name, options); const readSnippet = (from: string): Code => { - if ( + if ((isLong(field) || isLongValueType(field)) && options.forceLong === LongOption.LONG) { + return code`Long.fromValue(${from})`; + } else if ( isPrimitive(field) || (isTimestamp(field) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING)) || isValueType(ctx, field) @@ -1148,6 +1157,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return code`${from}`; } else if (isEnum(valueType)) { return code`${from} as number`; + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`Long.fromValue(${from})`; } else { const cstr = capitalize(basicTypeName(ctx, valueType).toCodeString()); return code`${cstr}(${from})`; @@ -1203,15 +1214,6 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${v} }; } `); - } else if ((isLong(field) || isLongValueType(field)) && options.forceLong === LongOption.LONG) { - const v = readSnippet(`object.${fieldName}`); - const type = basicTypeName(ctx, field); - chunks.push(code`if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`); - chunks.push(code`message.${fieldName} = ${v} as ${type};`); - chunks.push(code`} else {`); - const fallback = isWithinOneOf(field) ? 'undefined' : defaultValue(ctx, field); - chunks.push(code`message.${fieldName} = ${fallback}`); - chunks.push(code`}`); } else if (readSnippet(`x`).toCodeString() == 'x') { // An optimized case of the else below that works when `readSnippet` returns the plain input const fallback = isWithinOneOf(field) ? 'undefined' : defaultValue(ctx, field);