113 lines
4.5 KiB
Python
113 lines
4.5 KiB
Python
from my.core import __NOT_HPI_MODULE__
|
|
|
|
# NOTE: this tool was quite useful https://github.com/aj3423/aproto
|
|
|
|
from google.protobuf import descriptor_pool, descriptor_pb2, message_factory
|
|
|
|
TYPE_STRING = descriptor_pb2.FieldDescriptorProto.TYPE_STRING
|
|
TYPE_BYTES = descriptor_pb2.FieldDescriptorProto.TYPE_BYTES
|
|
TYPE_UINT64 = descriptor_pb2.FieldDescriptorProto.TYPE_UINT64
|
|
TYPE_MESSAGE = descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE
|
|
|
|
OPTIONAL = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
|
|
REQUIRED = descriptor_pb2.FieldDescriptorProto.LABEL_REQUIRED
|
|
|
|
|
|
def get_place_protos():
|
|
f1 = descriptor_pb2.DescriptorProto(name='xf1')
|
|
# TODO 2 -> 5 is address? 2 -> 6 is a pair of coordinates
|
|
f1.field.add(name='title', number=3, type=TYPE_STRING, label=REQUIRED)
|
|
f1.field.add(name='note' , number=4, type=TYPE_STRING, label=OPTIONAL)
|
|
# TODO what's the difference between required and optional? doesn't impact decoding?
|
|
|
|
ts = descriptor_pb2.DescriptorProto(name='Timestamp')
|
|
ts.field.add(name='seconds', number=1, type=TYPE_UINT64, label=REQUIRED)
|
|
ts.field.add(name='nanos' , number=2, type=TYPE_UINT64, label=REQUIRED)
|
|
|
|
f1.field.add(name='created', number=10 ,type=TYPE_MESSAGE, label=REQUIRED, type_name=ts.name)
|
|
f1.field.add(name='updated', number=11 ,type=TYPE_MESSAGE, label=REQUIRED, type_name=ts.name)
|
|
|
|
f2 = descriptor_pb2.DescriptorProto(name='xf2')
|
|
f2.field.add(name='addr1', number=2, type=TYPE_STRING, label=REQUIRED)
|
|
f2.field.add(name='addr2', number=3, type=TYPE_STRING, label=REQUIRED)
|
|
f2.field.add(name='f21' , number=4, type=TYPE_BYTES , label=REQUIRED)
|
|
f2.field.add(name='f22' , number=5, type=TYPE_UINT64, label=REQUIRED)
|
|
f2.field.add(name='f23' , number=6, type=TYPE_STRING, label=REQUIRED)
|
|
# NOTE: this also contains place ID
|
|
|
|
f3 = descriptor_pb2.DescriptorProto(name='xf3')
|
|
# NOTE: looks like it's the same as 'updated' from above??
|
|
f3.field.add(name='f31', number=1, type=TYPE_UINT64, label=OPTIONAL)
|
|
|
|
descriptor_proto = descriptor_pb2.DescriptorProto(name='PlaceParser')
|
|
descriptor_proto.field.add(name='f1', number=1, type=TYPE_MESSAGE, label=REQUIRED, type_name=f1.name)
|
|
descriptor_proto.field.add(name='f2', number=2, type=TYPE_MESSAGE, label=REQUIRED, type_name=f2.name)
|
|
descriptor_proto.field.add(name='f3', number=3, type=TYPE_MESSAGE, label=OPTIONAL, type_name=f3.name)
|
|
descriptor_proto.field.add(name='f4', number=4, type=TYPE_STRING , label=OPTIONAL)
|
|
# NOTE: f4 is the list id
|
|
|
|
return [descriptor_proto, ts, f1, f2, f3]
|
|
|
|
|
|
def get_labeled_protos():
|
|
address = descriptor_pb2.DescriptorProto(name='address')
|
|
# 1: address
|
|
# 2: parts of address (multiple)
|
|
# 3: full address
|
|
address.field.add(name='full', number=3, type=TYPE_STRING, label=REQUIRED)
|
|
|
|
main = descriptor_pb2.DescriptorProto(name='LabeledParser')
|
|
# field 1 contains item type and item id
|
|
main.field.add(name='title' , number=3, type=TYPE_STRING , label=REQUIRED)
|
|
main.field.add(name='address', number=5, type=TYPE_MESSAGE, label=OPTIONAL, type_name=address.name)
|
|
|
|
return [main, address]
|
|
|
|
|
|
def get_list_protos():
|
|
f1 = descriptor_pb2.DescriptorProto(name='xf1')
|
|
f1.field.add(name='name', number=5, type=TYPE_STRING, label=REQUIRED)
|
|
|
|
main = descriptor_pb2.DescriptorProto(name='ListParser')
|
|
main.field.add(name='f1', number=1, type=TYPE_MESSAGE, label=REQUIRED, type_name=f1.name)
|
|
main.field.add(name='f2', number=2, type=TYPE_STRING , label=REQUIRED)
|
|
|
|
return [main, f1]
|
|
|
|
|
|
def make_parser(main, *extras):
|
|
file_descriptor_proto = descriptor_pb2.FileDescriptorProto(name='dynamic.proto', package='dynamic_package')
|
|
for proto in [main, *extras]:
|
|
file_descriptor_proto.message_type.add().CopyFrom(proto)
|
|
|
|
pool = descriptor_pool.DescriptorPool()
|
|
file_descriptor = pool.Add(file_descriptor_proto)
|
|
|
|
message_descriptor = pool.FindMessageTypeByName(f'{file_descriptor_proto.package}.{main.name}')
|
|
factory = message_factory.MessageFactory(pool)
|
|
dynamic_message_class = factory.GetPrototype(message_descriptor)
|
|
|
|
return dynamic_message_class
|
|
|
|
|
|
place_parser_class = make_parser(*get_place_protos())
|
|
labeled_parser_class = make_parser(*get_labeled_protos())
|
|
list_parser_class = make_parser(*get_list_protos())
|
|
|
|
|
|
def parse_place(blob: bytes):
|
|
m = place_parser_class()
|
|
m.ParseFromString(blob)
|
|
return m
|
|
|
|
|
|
def parse_labeled(blob: bytes):
|
|
m = labeled_parser_class()
|
|
m.ParseFromString(blob)
|
|
return m
|
|
|
|
|
|
def parse_list(blob: bytes):
|
|
msg = list_parser_class()
|
|
msg.ParseFromString(blob)
|
|
return msg
|