Skip to content

Commit

Permalink
feat: allow overriding bytes as string
Browse files Browse the repository at this point in the history
Allows interperting a `bytes` field as a `string` type in JavaScript:

```protobuf
message MyMessage {
  bytes field = 1 [jstype = JS_STRING];
}
```
  • Loading branch information
achingbrain committed Oct 11, 2024
1 parent ad67ad4 commit 4eaccbe
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 12 deletions.
40 changes: 30 additions & 10 deletions packages/protons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,26 @@ const types: Record<string, string> = {
uint64: 'bigint'
}

const jsTypeOverrides: Record<string, 'number' | 'string'> = {
JS_NUMBER: 'number',
JS_STRING: 'string'
const jsTypeOverrides: Record<string, { type: 'number' | 'string', allowed: string[] }> = {
JS_NUMBER: {
type: 'number',
allowed: ['int64', 'uint64', 'sint64', 'fixed64', 'sfixed64']
},
JS_STRING: {
type: 'string',
allowed: ['int64', 'uint64', 'sint64', 'fixed64', 'sfixed64', 'bytes']
}
}

const encoderGenerators: Record<string, (val: string, jsTypeOverride?: 'number' | 'string') => string> = {
bool: (val) => `w.bool(${val})`,
bytes: (val) => `w.bytes(${val})`,
bytes: (val, jsTypeOverride) => {
if (jsTypeOverride === 'string') {
return `w.int64String(${val})`
}

return `w.bytes(new TextEncoder().encode(${val}))`

Check warning on line 265 in packages/protons/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/src/index.ts#L261-L265

Added lines #L261 - L265 were not covered by tests
},
double: (val) => `w.double(${val})`,
fixed32: (val) => `w.fixed32(${val})`,
fixed64: (val, jsTypeOverride) => {
Expand Down Expand Up @@ -319,7 +331,13 @@ const encoderGenerators: Record<string, (val: string, jsTypeOverride?: 'number'

const decoderGenerators: Record<string, (jsTypeOverride?: 'number' | 'string') => string> = {
bool: () => 'reader.bool()',
bytes: () => 'reader.bytes()',
bytes: (jsTypeOverride) => {
if (jsTypeOverride === 'string') {
return 'new TextDecoder().decode(reader.bytes())'
}

return 'reader.bytes()'

Check warning on line 339 in packages/protons/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/src/index.ts#L335-L339

Added lines #L335 - L339 were not covered by tests
},
double: () => 'reader.double()',
fixed32: () => 'reader.fixed32()',
fixed64: (jsTypeOverride) => {
Expand Down Expand Up @@ -432,12 +450,14 @@ const defaultValueTestGeneratorsJsTypeOverrides: Record<string, (field: string)
}

function findJsTypeOverride (defaultType: string, fieldDef: FieldDef): 'number' | 'string' | undefined {
if (fieldDef.options?.jstype != null && jsTypeOverrides[fieldDef.options?.jstype] != null) {
if (!['int64', 'uint64', 'sint64', 'fixed64', 'sfixed64'].includes(defaultType)) {
throw new Error(`jstype is only allowed on int64, uint64, sint64, fixed64 or sfixed64 fields - got "${defaultType}"`)
const override = jsTypeOverrides[fieldDef.options?.jstype]

if (override != null) {
if (!override.allowed.includes(defaultType)) {
throw new Error(`jstype is only allowed on ${override.allowed.join(', ')} fields - got "${defaultType}"`)

Check warning on line 457 in packages/protons/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/src/index.ts#L453-L457

Added lines #L453 - L457 were not covered by tests
}

return jsTypeOverrides[fieldDef.options?.jstype]
return override.type

Check warning on line 460 in packages/protons/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/src/index.ts#L460

Added line #L460 was not covered by tests
}
}

Expand Down Expand Up @@ -517,7 +537,7 @@ function createDefaultObject (fields: Record<string, FieldDef>, messageDef: Mess
defaultValueGenerator = defaultValueGeneratorsJsTypeOverrides[jsTypeOverride]
}

if (type === 'bytes') {
if (type === 'bytes' && jsTypeOverride !== 'string') {

Check warning on line 540 in packages/protons/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/src/index.ts#L540

Added line #L540 was not covered by tests
moduleDef.addImport('uint8arrays/alloc', 'alloc', 'uint8ArrayAlloc')
}

Expand Down
3 changes: 2 additions & 1 deletion packages/protons/test/custom-options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ describe('custom options', () => {
ui64: '5',
si64: '5',
f64: '5',
sf64: '5'
sf64: '5',
bytes: ''
}

expect(CustomOptionString.decode(CustomOptionString.encode(obj)))
Expand Down
1 change: 1 addition & 0 deletions packages/protons/test/fixtures/custom-option-jstype.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ message CustomOptionString {
sint64 si64 = 4 [jstype = JS_STRING];
fixed64 f64 = 5 [jstype = JS_STRING];
sfixed64 sf64 = 6 [jstype = JS_STRING];
bytes bytes = 7 [jstype = JS_STRING];
}
13 changes: 12 additions & 1 deletion packages/protons/test/fixtures/custom-option-jstype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export interface CustomOptionString {
si64: string
f64: string
sf64: string
bytes: string
}

export namespace CustomOptionString {
Expand Down Expand Up @@ -171,6 +172,11 @@ export namespace CustomOptionString {
w.sfixed64String(obj.sf64)
}

if ((obj.bytes != null && obj.bytes !== '')) {
w.uint32(58)
w.int64String(obj.bytes)
}

Check warning on line 178 in packages/protons/test/fixtures/custom-option-jstype.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/test/fixtures/custom-option-jstype.ts#L176-L178

Added lines #L176 - L178 were not covered by tests

if (opts.lengthDelimited !== false) {
w.ldelim()
}
Expand All @@ -181,7 +187,8 @@ export namespace CustomOptionString {
ui64: '',
si64: '',
f64: '',
sf64: ''
sf64: '',
bytes: ''
}

const end = length == null ? reader.len : reader.pos + length
Expand Down Expand Up @@ -214,6 +221,10 @@ export namespace CustomOptionString {
obj.sf64 = reader.sfixed64String()
break
}
case 7: {
obj.bytes = new TextDecoder().decode(reader.bytes())
break
}

Check warning on line 227 in packages/protons/test/fixtures/custom-option-jstype.ts

View check run for this annotation

Codecov / codecov/patch

packages/protons/test/fixtures/custom-option-jstype.ts#L225-L227

Added lines #L225 - L227 were not covered by tests
default: {
reader.skipType(tag & 7)
break
Expand Down

0 comments on commit 4eaccbe

Please sign in to comment.