Skip to content

Instantly share code, notes, and snippets.

@Totktonada
Last active January 9, 2024 16:30
Show Gist options
  • Save Totktonada/310dea47d65c926c6cb35d9f9f50bd3b to your computer and use it in GitHub Desktop.
Save Totktonada/310dea47d65c926c6cb35d9f9f50bd3b to your computer and use it in GitHub Desktop.
ProtocolBuffers encoder/decoder Lua module API proposal
local protobuf = require('protobuf')
-- The schema examples are from etcd.
--
-- https://github.com/etcd-io/etcd/blob/v3.5.11/api/mvccpb/kv.proto
-- https://github.com/etcd-io/etcd/blob/v3.5.11/api/etcdserverpb/rpc.proto
-- Message fields
-- --------------
-- var1
protobuf.field('bytes', 'key', 1)
-- var2 & var3
-- no explicit field object
-- => (working/serialize representation)
{
type = 'bytes',
name = 'key',
id = 1,
}
-- Message
-- -------
-- var1
protobuf.message('KeyValue', {
protobuf.field('bytes', 'key', 1),
protobuf.field('int64', 'create_revision', 2),
protobuf.field('int64', 'mod_revision', 3),
protobuf.field('int64', 'version', 4),
protobuf.field('bytes', 'value', 5),
protobuf.field('int64', 'lease', 6),
})
-- var2
protobuf.message('KeyValue', {
[1] = {'bytes', 'key'},
[2] = {'int64', 'create_revision'},
[3] = {'int64', 'mod_revision'},
[4] = {'int64', 'version'},
[5] = {'bytes', 'value'},
[6] = {'int64', 'lease'},
})
-- var3
protobuf.message('KeyValue', {
key = {'bytes', 1},
create_revision = {'int64', 2},
mod_revision = {'int64', 3},
version = {'int64', 4},
value = {'bytes', 5},
lease = {'int64', 6},
})
-- => (working/serialize representation)
{
type = 'message',
name = 'KeyValue',
field_by_name = {
key = {
type = 'bytes',
name = 'key',
id = 1,
},
create_revision = {
type = 'int64',
name = 'create_revision',
id = 2,
},
mod_revision = {
type = 'int64',
name = 'mod_revision',
id = 3,
},
version = {
type = 'int64',
name = 'version',
id = 4,
},
value = {
type = 'bytes',
name = 'value',
id = 5,
},
lease = {
type = 'int64',
name = 'lease',
id = 6,
},
},
field_by_id = {
[1] = {
type = 'bytes',
name = 'key',
id = 1,
},
[2] = {
type = 'int64',
name = 'create_revision',
id = 2,
},
[3] = {
type = 'int64',
name = 'mod_revision',
id = 3,
},
[4] = {
type = 'int64',
name = 'version',
id = 4,
},
[5] = {
type = 'bytes',
name = 'value',
id = 5,
},
[6] = {
type = 'int64',
name = 'lease',
id = 6,
},
},
}
-- Enum
-- ----
-- var1
protobuf.enum('EventType', {
protobuf.enum_value('PUT', 0),
protobuf.enum_value('DELETE', 1),
})
-- var2
protobuf.enum('EventType', {
[0] = 'PUT',
[1] = 'DELETE',
})
-- var3
protobuf.enum('EventType', {
['PUT'] = 0,
['DELETE'] = 1,
})
-- => (working/serialize representation)
{
type = 'enum',
name = 'EventType',
value_by_name = {
['PUT'] = 0,
['DELETE'] = 1,
},
value_by_id = {
[0] = 'PUT',
[1] = 'DELETE',
},
}
-- A field of a message type
-- -------------------------
-- var1
protobuf.message('Event', {
protobuf.field('bytes', 'type', 1),
protobuf.field('KeyValue', 'kv', 2),
protobuf.field('KeyValue', 'prev_kv', 3),
})
-- var2
protobuf.message('Event', {
[1] = {'bytes', 'type'},
[2] = {'KeyValue', 'kv'},
[3] = {'KeyValue', 'prev_kv'},
})
-- var3
protobuf.message('Event', {
type = {'bytes', 1},
kv = {'KeyValue', 2},
prev_kv = {'KeyValue', 3},
})
-- => (working/serialize representation)
{
type = 'message',
name = 'Event',
field_by_name = {
type = {
type = 'bytes',
name = 'key',
id = 1,
},
kv = {
type = 'KeyValue',
name = 'kv',
id = 2,
},
prev_kv = {
type = 'KeyValue',
name = 'prev_kv',
id = 3,
},
},
field_by_id = {
[1] = {
type = 'bytes',
name = 'key',
id = 1,
},
[2] = {
type = 'KeyValue',
name = 'kv',
id = 2,
},
[3] = {
type = 'KeyValue',
name = 'prev_kv',
id = 3,
},
},
}
-- Repeated field
-- --------------
-- var1
protobuf.message('RangeResponse', {
protobuf.field('ResponseHeader', 'header', 1),
protobuf.repeated('KeyValue', 'kvs', 2), -- !!
protobuf.field('bool', 'more', 3),
protobuf.field('int64', 'count', 4),
})
-- var2
protobuf.message('RangeResponse', {
[1] = {'ResponseHeader', 'header'}
[2] = {'repeated KeyValue', 'kvs'}, -- !!
[3] = {'bool', 'more'},
[4] = {'int64', 'count'},
})
-- var3
protobuf.message('RangeResponse', {
header = {'ResponseHeader', 1},
kvs = {'repeated KeyValue', 2}, -- !!
more = {'bool', 3},
count = {'int64', 4},
})
-- => (working/serialize representation)
{
type = 'message',
name = 'RangeResponse',
field_by_name = {
header = {
type = 'ResponseHeader',
name = 'header',
id = 1,
},
kvs = {
type = 'KeyValue',
name = 'kvs',
id = 2,
repeated = true, -- !!
},
more = {
type = 'bool',
name = 'more',
id = 3,
},
count = {
type = 'int64',
name = 'count',
id = 4,
},
},
field_by_value = {
[1] = {
type = 'ResponseHeader',
name = 'header',
id = 1,
},
[2] = {
type = 'KeyValue',
name = 'kvs',
id = 2,
repeated = true, -- !!
},
[3] = {
type = 'bool',
name = 'more',
id = 3,
},
[4] = {
type = 'int64',
name = 'count',
id = 4,
},
},
}
-- Protocol definition
-- -------------------
-- varA
protobuf.protocol({
protobuf.message(<...>),
protobuf.message(<...>),
protobuf.enum(<...>),
protobuf.enum(<...>),
})
-- varB
--
-- message/enum definitions are from var1/var2/var3
local proto = protobuf.protocol()
proto:message('MessageName_1', <..message def..>)
proto:enum('EnumName_1', <..enum def..>)
proto:consistency_check()
-- varC
--
-- message/enum objects are from var1/var2/var3
local proto = protobuf.protocol()
proto:add(protobuf.message(<...>))
proto:add(protobuf.enum(<...>))
proto:consistency_check()
-- => (working/serialize representation)
{
['MessageName_1'] = {
type = 'message',
name = 'MessageName_1',
field_by_name = <...>,
field_by_id = <...>,
},
['EnumName_1'] = {
type = 'enum',
name = 'EnumName_1',
value_by_name = <...>,
value_by_id = <...>,
},
}
-- Protocol object methods
-- -----------------------
proto:encode('MessageName', <..data..>)
proto:encode('EnumName', <..data..>)
proto:encode('int64', <..data..>)
proto:encode('bytes', <..data..>)
-- <...>
proto:decode('MessageName', <string>)
proto:decode('EnumName', <string>)
proto:decode('int64', <string>)
proto:decode('bytes', <string>)
-- <...>
-- Module level functions
-- ----------------------
protobuf.protocol()
protobuf.field() -- varA + var1, varC + var1
protobuf.message() -- varA, varC
protobuf.enum() -- varA, varC
-- varX
-- No module level consistency_check/encode/decode functions.
-- varY
protobuf.consistency_check(<protocol>)
protobuf.encode(<protocol>, <message/enum/scalar name>, <..data..>)
protobuf.decode(<protocol>, <message/enum/scalar name>, <string>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment