diff --git a/doc/api.md b/doc/api.md index 465ed66..fe8a76f 100644 --- a/doc/api.md +++ b/doc/api.md @@ -56,7 +56,8 @@ Create a serializer of `mainType` defined in `proto`. This is a Transform stream ### Serializer.createPacketBuffer(packet) -Returns a buffer of the `packet`. +Returns a buffer of the `packet`. If serialization fails and `packet.name` is set, the error message is prefixed with +`in packet : ` unless the error's field path already contains that name. ## Parser(proto,mainType) diff --git a/src/serializer.js b/src/serializer.js index 8b52e95..5d9bd5f 100644 --- a/src/serializer.js +++ b/src/serializer.js @@ -9,7 +9,16 @@ class Serializer extends Transform { } createPacketBuffer (packet) { - return this.proto.createPacketBuffer(this.mainType, packet) + try { + return this.proto.createPacketBuffer(this.mainType, packet) + } catch (e) { + // A compiled protocol reports no field path, so the packet name is the only way to say which packet failed + const name = packet?.name + if (name != null && !String(e.field ?? '').split('.').includes(String(name))) { + e.message = `in packet ${name}: ${e.message}` + } + throw e + } } _transform (chunk, enc, cb) { diff --git a/test/misc.js b/test/misc.js index dab6359..5224db1 100644 --- a/test/misc.js +++ b/test/misc.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ const assert = require('assert') -const { ProtoDef } = require('../') +const { ProtoDef, Serializer } = require('../') const { ProtoDefCompiler } = require('../').Compiler it('example works', () => { @@ -25,3 +25,32 @@ describe('mapper', () => { }) } }) + +describe('Serializer', () => { + const types = { + packet_position: ['container', [{ name: 'x', type: 'f64' }, { name: 'face', type: ['mapper', { type: 'varint', mappings: { 0: 'down' } }] }]], + packet: ['container', [ + { name: 'name', type: ['mapper', { type: 'varint', mappings: { 0: 'position' } }] }, + { name: 'params', type: ['switch', { compareTo: 'name', fields: { position: 'packet_position' } }] } + ]] + } + const proto = new ProtoDef() + proto.addTypes(types) + const compiler = new ProtoDefCompiler() + compiler.addTypesToCompile(types) + const compiled = compiler.compileProtoDefSync() + const bad = { name: 'position', params: { x: 1, face: 'up' } } + + it('names the packet when the error has no field path (compiled)', () => { + assert.throws(() => new Serializer(compiled, 'packet').createPacketBuffer(bad), + { message: 'in packet position: SizeOf error for undefined : up is not in the mappings value' }) + }) + it('does not repeat a packet name already in the field path (interpreted)', () => { + assert.throws(() => new Serializer(proto, 'packet').createPacketBuffer(bad), + { message: 'SizeOf error for params.position.face : up is not in the mappings value' }) + }) + it('leaves errors alone when the value has no name', () => { + assert.throws(() => new Serializer(compiled, 'packet_position').createPacketBuffer(bad.params), + { message: 'SizeOf error for undefined : up is not in the mappings value' }) + }) +})