# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['protobuf2arr']

package_data = \
{'': ['*']}

install_requires = \
['protobuf>=3.20.0,<4.0.0', 'simplejson>=3.17.6,<4.0.0']

setup_kwargs = {
    'name': 'protobuf2arr',
    'version': '0.1.2',
    'description': "Translate a protobuf message to Google's RPC array format.",
    'long_description': '# protobuf2arr\n Translate a protobuf message to Google\'s RPC array format. Many non-public  Google services use an array wire format for protobuf messages where each field number is mapped to an index of an array (one-based).\n\n# Simple Example\n The following protobuf message `Message` gets translated to the array: `[field1_value, field2_value]`.\n \n Protobuf implementation:\n ```\nsyntax = "proto3";\n\nmessage Message {\n    int32 field1 = 1;\n    string field2 = 2;\n}\n ```\n\nPython implementation:\n ```\nimport message_pb2\nmessage = message_pb2.Message()\nmessage.field1 = 77\nmessage.field2 = "Hello World"\n ```\n\nRPC array format:\n```\n[77, "Hello World"]\n```\n\n## Skipping field numbers \nSince fields are mapped to indexes of an array, missing field numbers are included as `None` or `null` types. \n\nLet\'s add a new field `field3` to the protobuf message above, but with a number of `4`, skipping `3`.\n```\nsyntax = "proto3";\n\nmessage Message {\n    int32 field1 = 1;\n    string field2 = 2;\n    bool field3 = 4;\n}\n```\n\nPython implementation remains the same, with the addition of enabling `field3`.\n```\nmessage.field3 = True\n```\n\nRPC array format now includes `None` or `null` (serialized string) for the missing field `3` and the value of `field3` in the array position `4` (one-based). \n```\n[77, "Hello World", null, True]\n```\n\n# Installation\n\nYou can install the package directly from pypi:\n```\npip install protobuf2arr\n```\n\n# Usage\nAssuming we have a protobuf message format defined as such:\n```\nsyntax = "proto3";\n\npackage mypackage;\n\nmessage TaskQueue {\n    int32 queue_id = 1;\n    string queue_name = 2;\n    repeated TaskItem items = 3;\n\n    message TaskItem {\n        int32 task_id = 1;\n        string task_name = 2;\n        string date = 4; \n    }\n}\n```\n\nThe message is transformed via `protoc` to `taskqueue_pb2.py`. Normal protobuf usage applies to the generated message:\n```\nimport taskqueue_pb2\n\ntask_queue = taskqueue_pb2.TaskQueue()\ntask_queue.queue_id = 78\ntask_queue.queue_name = "redis:thread:tasks"\n\nfor i in range(2):\n    task = taskqueue_pb2.TaskQueue.TaskItem()\n    task.task_id = i + 1\n    task.task_name = f"cleanup:{i + 1}"\n    task.date = \'2022-04-01\'\n    task_queue.items.append(task)\n\nprint(task_queue)\n```\n\nThis produces the standardize protobuf string representation:\n```\nqueue_id: 78\nqueue_name: "redis:thread:tasks"\nitems {\n  task_id: 1\n  task_name: "cleanup:1"\n  date: "2022-04-01"\n}\nitems {\n  task_id: 2\n  task_name: "cleanup:2"\n  date: "2022-04-01"\n}\n```\n\nHowever, when we serialize the protobuf message to an array `serialize_msg2arr(task_queue)`, we get the following:\n```\n[78, \'redis:thread:tasks\', [[1, \'cleanup:1\', \'2022-04-01\'], [2, \'cleanup:2\', \'2022-04-01\']]]\n```\n\n# Nullable Types\nConsider the empty state for the task queue with a single empty task.\n```\ntask_queue = taskqueue_pb2.TaskQueue()\ntask_queue.items.append(taskqueue_pb2.TaskQueue.TaskItem())\nserialize_msg2arr(task_queue)\n```\n```\n[0, \'\', [[0, \'\', \'\']]]\n```\nProtobuf fields empty state is their type default, so `0` for `int32` and the empty string for `string`. However, many Google api\'s require `None` or `null` for field empty states. \n\nThis library allows you to include a `nullable` option on any field where the default value should be translated to a `None` type. The `nullable` custom option can be included either of two ways in your protobuf schema, the serializer only cares about the option name.\n\n## Inline declaration\nDeclare the custom option inline in your proto file:\n```\nimport "google/protobuf/descriptor.proto";\n\nextend google.protobuf.FieldOptions {\n    optional string nullable = 50027;\n}\n```\nThe option extension number does not matter, so long as it is within the proper Protobuf field option range. Now you to tag your fields with this option and the specific value (`string`) you want to be parsed to `None` as such:\n```\nint32 queue_id = 1 [(nullable) = \'0\'];\n```\n\n## Package declaration\nAlteratively, you can define this custom option in a separate proto file and import it into your message.\n\nnullable_option.proto:\n```\nsyntax = "proto3";\n\npackage protobuf2arr;\n\nimport "google/protobuf/descriptor.proto";\n\nextend google.protobuf.FieldOptions {\n    optional string nullable = 50027;\n}\n```\n\nmessage.proto:\n```\nsyntax = "proto3";\n\npackage mypackage;\n\nimport "nullable_option.proto"\n\nmessage TaskQueue {\n    int32 queue_id = 1 [(nullable_option.nullable) = \'0\'];\n}\n```\n\n## Output\nAssume that we want to ignore the `queue_name` and `date` fields and send the `None` or `null` type when they are not set, we can use protobuf custom field options. Regardless of which method is choosen, we get the results:\n\nmessage.py:\n```\ntask_queue = taskqueue_pb2.TaskQueue()\ntask_queue.items.append(taskqueue_pb2.TaskQueue.TaskItem())\nserialize_msg2arr(task_queue)\n```\noutput:\n```\n[null, \'\', [[0, \'\', null]]]\n```\n\n# Nullable Messages\nSet the `nullable` tag to the empty message state `\'\'` to support lists of messages where an item is `None` i.e the default state. Alternatively, more complicated default states are supported:\n\nmessage.proto:\n```\nrepeated TaskItem items = 3 [(nullable) = \'task_id: 1\'];\n```\n\nmessage.py:\n```\ntask_queue = taskqueue_pb2.TaskQueue()\ntask = taskqueue_pb2.TaskQueue.TaskItem()\ntask.task_id = 1\ntask_queue.items.append(task)\nserialize_msg2arr(task_queue)\n```\n\noutput:\n```\n[null, \'\', [null]]\n```',
    'author': 'Kevin Ramdath',
    'author_email': 'krpent@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/minormending/protobuf2arr',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.10,<4.0',
}


setup(**setup_kwargs)
