Last active
January 9, 2024 16:30
-
-
Save Totktonada/310dea47d65c926c6cb35d9f9f50bd3b to your computer and use it in GitHub Desktop.
ProtocolBuffers encoder/decoder Lua module API proposal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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