{"id":17258766,"url":"https://github.com/helly25/proto","last_synced_at":"2025-03-26T09:21:27.651Z","repository":{"id":181459538,"uuid":"666808359","full_name":"helly25/proto","owner":"helly25","description":"proto: A collection of utilities around Google Protocolbuffers.","archived":false,"fork":false,"pushed_at":"2025-03-23T20:48:13.000Z","size":51354,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-23T21:25:07.422Z","etag":null,"topics":["cpp","cpp20","cpp20-library","googletest","matchers","proto","protobuf","protocol-buffers"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/helly25.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-15T16:38:13.000Z","updated_at":"2025-03-23T20:48:16.000Z","dependencies_parsed_at":"2024-01-20T10:42:33.602Z","dependency_job_id":"8994bdd6-78b0-4697-92ed-c1fa4b48a4eb","html_url":"https://github.com/helly25/proto","commit_stats":null,"previous_names":["helly25/proto"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helly25%2Fproto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helly25%2Fproto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helly25%2Fproto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helly25%2Fproto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/helly25","download_url":"https://codeload.github.com/helly25/proto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245623030,"owners_count":20645681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp20","cpp20-library","googletest","matchers","proto","protobuf","protocol-buffers"],"created_at":"2024-10-15T07:21:54.674Z","updated_at":"2025-03-26T09:21:27.643Z","avatar_url":"https://github.com/helly25.png","language":"C++","readme":"This package contains a collection of utilities around Google's [Protocolbuffer](https://github.com/protocolbuffers/protobuf). The functions offered in this packages are widely used across Google's C++ code base and have saved tens of thousands of engineering hours. Some of these functons were originally implemented by the author and later re-implemented or cloned (see below).\n\nThe project works with Google's proto library version 27, 28, 29 and 30. Packages are available at [Bazel Central Registry](https://registry.bazel.build/modules/helly25_proto) and [Github](https://github.com/helly25/proto/releases).\n\n[![Test](https://github.com/helly25/proto/actions/workflows/main.yml/badge.svg)](https://github.com/helly25/proto/actions/workflows/main.yml)\n\n# Parse Proto\n\n* rule: `@com_helly25_proto//mbo/proto:parse_text_proto_cc`\n* namespace: `mbo::proto`\n\n* `ParseTextProtoOrDie`(`text_proto` [, `std::source_location`])\n  * `text_proto` is a text proto best identified as a raw-string with marker `pb` or `proto`.\n  * If `text_proto` cannot be parsed into the receiving proto type, then the function will fail.\n  * Prefer this function over template function `ParseTextOrDie`.\n\n* `ParseTextOrDie`\u003c`Proto`\u003e(`text_proto` [, `std::source_location`])\n  * `text_proto` is a text proto best identified as a raw-string with marker `pb` or `proto`.\n  * `Proto` is the type to produce.\n  * If `text_proto` cannot be parsed as a `Proto`, then the function will fail.\n  * Use this only if the `Proto` type cannot be inferred by `ParserTextProtoOrDie`.\n\n* `ParseTest`\u003c`Proto`\u003e(`text_proto` [, `std::source_location`]) -\u003e `absl::StatusOr`\u003c`Proto`\u003e\n  * `text_proto` is a text proto best identified as a raw-string with marker `pb` or `proto`.\n  * `Proto` is the type to produce.\n  * If `text_proto` cannot be parse as a `Proto`, then the function returns a non-`absl::OkStatus`.\n  * Use this function in cases where errors are expected.\n\n## Usage\n\nBUILD.bazel:\n\n```bzl\ncc_test(\n    name = \"test\",\n    srcs = [\"test.cc\"],\n    deps = [\"@com_helly25_proto//mbo/proto:parse_text_proto\",]\n)\n```\n\nSource test.cc:\n\n```c++\n#include \"mbo/proto/parse_text_proto.h\"\n\nusing ::mbo::proto::ParseTextProtoOrDie;\n\nTEST(Foo, Test) {\n    MyProto msg = ParseTextProtoOrDie(R\"pb(\n      field: \"name\"\n      what: \"Just dump the plain text-proto as C++ raw-string.\"\n      )pb\");\n    // ...\n}\n```\n\nNote:\n* In the above example the proto is not manually constructed field by field.\n* Instead the text-proto output is directly used as a C++ raw-string.\n* Further the C++ raw-string is enclosed in `pb` markers which some tidy tools identify and use to correctly format the text-proto.\n* One of the biggest advantages of these parse function is that their result can be assigned into a const variable.\n\nThe `ParseTextProtoOrDie` function dies if the input text-proto is not valid. That is done because the function emulates type safety this way. That is the author will likely only have to fix this once while many people will read the code. Further, this is test input that is supposed to be correct as is. If the input is of dynamic nature, then `ParseText\u003cProtoType\u003e(std::string_view)` has to be used.\n\n# Proto Matchers\n\n* rule: `@com_helly25_proto//mbo/proto:matchers_cc`\n* namespace: `mbo::proto`\n\n* `EqualsProto`(`msg`)\n  * `msg`: protocolbuffer Message or string\n  * Checks whether `msg` and the argument are the same proto.\n  * If a string is used it is advisable to format the string as a raw-string\n    with 'pb' marker as demonstrated above.\n\n* `EqualsProto`()\n  * 2-tuple polymorphic matcher that can be used for container comparisons.\n\n* `EquivToProto`(`msg`)\n  * `msg`: protocolbuffer Message or string\n  * Similar to `EqualsProto` but checks whether `msg` and the argument are equivalent.\n  * Equivalent means that if one side sets a field to the default value and the other side does not\n    have that field specified, then they are equivalent.\n\n* `EquivToProto`()\n  * 2-tuple polymorphic matcher that can be used for container comparisons.\n\n## Proto Matcher Wrappers\n\n* `Approximately`(`matcher` [, `margin` [, `fraction`]])\n  * `matcher` wrapper that allows to compare `double` and `float` values with a margin of error.\n  * optional `margin` of error and a relative `fraction` of error which will make values match if\n    satisfied.\n\n* `TreatingNaNsAsEqual`(`matcher`)\n  * `matcher` wrapper that compares floating-point fields such that NaNs are equal\n\n* `IgnoringFields`(`ignore_fields`, `matcher`)\n  * `matcher` wrapper that allows to ignore fields with different values.\n  * `ignore_fields` list of fields to ignore. Fields are specified by their fully qualified names,\n    i.e., the names corresponding to FieldDescriptor.full_name(). (e.g.\n    testing.internal.FooProto2.member).\n\n* `IgnoringFieldPaths`(`ignore_field_paths`, `matcher`)\n  * `matcher` wrapper that allows to ignore fields with different values by their paths.\n  * `ignore_field_paths` list of paths to ignore (e.g. 'field.sub_field.terminal_field').\n\n* `IgnoringRepeatedFieldOrdering`(`matcher`)\n  * `matcher` wrapper that allows to ignore the order in which repeated fields are presented.\n  * E.g.: `IgnoringRepeatedFieldOrdering(EqualsProto(R\"pb(x: 1 x: 2)pb\"))`: While the provided\n    proto has the repeated field `x` specified in the order `[1, 2]`, the matcher will also match\n    if the argument proto has the order reversed.\n\n* `Partially`(`matcher`)\n  * `matcher` wrapper that compares only fields present in the expected protobuf. For example,\n  * `Partially(EqualsProto(p))` will ignore any field that is not set in p when comparing the\n    protobufs.\n\n* `WhenDeserialized`(`matcher`)\n  * `matcher` wrapper that matches a string that can be deserialized as a protobuf that matches\n    `matcher`.\n\n* `WhenDeserializedAs`\u003c`Proto`\u003e(`matcher`)\n  * `matcher` wrapper that matches a string that can be deserialized as a protobuf of type `Proto`\n     that matches `matcher`.\n\n## Usage\n\nBUILD.bazel:\n\n```bzl\ncc_test(\n    name = \"test\",\n    srcs = [\"test.cc\"],\n    deps = [\"@com_helly25_proto//mbo/proto:matchers\"],\n)\n```\n\nSource test.cc:\n\n```c++\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"mbo/proto/matchers.h\"\n\nusing ::mbo::proto::EqualsProto;\nusing ::mbo::proto::IgnoringRepeatedFieldOrdering;\n\nTEST(Foo, EqualsProto) {\n    MyProto msg;\n    msg.set_field(\"name\");\n    EXPECT_THAT(msg, EqualsProto(R\"pb(\n      field: \"name\"\n      )pb\"));\n}\n```\n\nIn the above example `EqualsProto` takes the text-proto as a C++ raw-string.\n\nThe matchers can of course be combined with the parse functions. The below shows how a `FunctionUnderTest` can be tested. It receives the proto input directly from the parse function and the matcher compares it directly to the expected golden result text-proto. Note how there is no field-by-field processing anywhere. No dstraction from what is being tested and what the expectations are. Or in other words the test avoids misleading and error prone in-test logic. And becasue the function-under-test is called inside the EXPECT_THAT macro the gtest failure messages will show what actually failed (and not something like \"Input: temp_var\").\n\n```c++\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"mbo/proto/matchers.h\"\n#include \"mbo/proto/parse_text_proto.h\"\n\nusing ::mbo::proto::EqualsProto;\nusing ::mbo::proto::IgnoringRepeatedFieldOrdering;\nusing ::mbo::proto::ParseTextProtoOrDie;\n\nMyProto FunctionUnderTest(const MyProto\u0026 proto) {\n  return proto;\n}\n\nTEST(Foo, Wrapper) {\n    const MyProto input = ParseTextProtoOrDie(R\"pb(\n      number: 1\n      number: 2\n      number: 3\n    )pb\");\n    EXPECT_THAT(\n      FunctionUnderTest(input),\n      IgnoringRepeatedFieldOrdering(EqualsProto(R\"pb(\n        number: 1\n        number: 2\n        number: 3\n      )pb\")));\n}\n```\n\n# Proto Files\n\n* rule: `@com_helly25_proto//mbo/proto:file_cc`\n* namespace: `mbo::proto`\n\n* class `ReadBinaryProtoFile`(`filename`)\n  * Reads a binary proto file. Usually using `.pb` file extension.\n  * `ProtoType` the protocol buffer type to read.\n  * `filename` the filename to read from.\n  * Creates a type-erased type that reads the file on access.\n  * Supports method interface `As` and `OrDie` which take an explicit type argument.\n\n* class `ReadTextProtoFile`(`filename`)\n  * Reads a text proto file. Usually using `.textproto` file extension.\n  * `ProtoType` the protocol buffer type to read.\n  * `filename` the filename to read from.\n  * Creates a type-erased type that reads the file on access.\n  * Supports method interface `As` and `OrDie` which take an explicit type argument.\n\n* function `WriteBinaryProtoFile`(`filename`, `message`)\n  * Writes a binary proto file. Usually using `.pb` file extension.\n  * `filename` the filename to read from.\n  * `message` the protocol buffer to write.\n  * Returns `absl::OkStatus()` or an error status.\n\n* function `WriteTextProtoFile`(`filename`, `message`)\n  * Writes a text proto file. Usually using `.textproto` file extension.\n  * `filename` the filename to read from.\n  * `message` the protocol buffer to write.\n  * Returns `absl::OkStatus()` or an error status.\n\n* function `HasBinaryProtoExtension`(`filesname`)\n  * Returns whether the filename ends with a well-known extension for binary proto files.\n\n* function `HasTextProtoExtension`(`filesname`)\n  * Returns whether the filename ends with a well-known extension for text proto files.\n\n## Usage\n\n```c++\n#include \u003cfilesystem\u003e\n#include \u003ciostream\u003e\n\n#include \"mbo/proto/file.h\"\n#include \"my_protos/my_proto.pb.h\"  # Containing `MyProto`\n\nusing ::mbo::proto::ReadBinaryProtoFile;\nusing ::mbo::proto::ReadTextProtoFile;\nusing ::mbo::proto::WriteBinaryProtoFile;\nusing ::mbo::proto::WriteTextProtoFile;\n\nint UseBinaryProto(const MyProto\u0026 my_proto, const std::filesystem::path\u0026 filename) {\n  const auto result = WriteBinaryProtoFile(filename, my_proto);\n  if (!auto.ok()) {\n    std::cerr \u003c\u003c \"Error: \" \u003c\u003c result.status() \u003c\u003c \"\\n\";\n    return 1;\n  }\n  // Reading with type-erased interface.\n  const absl::StatusOr\u003cMyProto\u003e proto_or_status = ReadBinaryProtoFile(filename);\n  if (!proto_or_status.ok()) {\n    std::cerr \u003c\u003c \"Error: \" \u003c\u003c proto_or_status.status() \u003c\u003c \"\\n\";\n    return 2;\n  }\n  // Reading with given template type parameter.\n  {\n    const auto proto_or_status = ReadBinaryProtoFile::As\u003cMyProto\u003e(filename);\n    const auto proto = ReadBinaryProtoFile::OrDie\u003cMyProto\u003e(filename);\n  }\n  return 0;\n}\n\nint UseTextProto(const MyProto\u0026 my_proto, const std::filesystem::path\u0026 filename) {\n  const auto result = WriteTextProtoFile(filename, my_proto);\n  if (!auto.ok()) {\n    std::cerr \u003c\u003c \"Error: \" \u003c\u003c result.status() \u003c\u003c \"\\n\";\n    return 4;\n  }\n  // Reading with type-erased interface.\n  const absl::StatusOr\u003cMyProto\u003e proto_or_status = ReadTextProtoFile(filename);\n  if (!proto_or_status.ok()) {\n    std::cerr \u003c\u003c \"Error: \" \u003c\u003c proto_or_status.status() \u003c\u003c \"\\n\";\n    return 8;\n  }\n  // Reading with given template type parameter.\n  {\n    const auto proto_or_status = ReadTextProtoFile::As\u003cMyProto\u003e(filename);\n    const auto proto = ReadTextProtoFile::OrDie\u003cMyProto\u003e(filename);\n  }\n  return 0;\n}\n\nint main() {\n  const MyProto my_proto;\n  return UseBinaryProto(my_proto, \"my_file.pb\") + UseTextProto(my_proto, \"my_file.textproto\");\n}\n```\n\n# Installation and requirements\n\nThis repository requires a C++20 compiler (in case of MacOS XCode 15 is needed). The project's CI tests a combination of Clang and GCC compilers on Linux/Ubuntu and MacOS. The project can be used with Google's proto libraries in versions [27, 28, 29, 30].\n\nThe reliance on a C++20 compiler is because it uses `std::source_location` since Google's Abseil `absl::SourceLocation` has not been open sourced.\n\nThe project only comes with a Bazel BUILD.bazel file and can be added to other Bazel projects.\n\nThe project is formatted with specific clang-format settings which require clang 16+ (in case of MacOs LLVM 16+ can be installed using brew). For simplicity in dev mode the project pulls the appropriate clang tools and can be compiled with those tools using `bazel [build|test] --config=clang ...`.\n\n## WORKSPACE\n\nCheckout [Releases](https://github.com/helly25/proto/releases) or use head ref as follows:\n\n```bzl\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n  name = \"com_helly25_proto\",\n  url = \"https://github.com/helly25/proto/releases/download/0.0.0/proto-0.0.0.tar.gz\",\n  sha256 = \"\", # See https://github.com/helly25/proto/releases for releases versions and SHA codes.\n)\n```\n\n## MODULES.bazel\n\nThe [BCR](https://registry.bazel.build/modules/helly25_proto) version has its dependencies pushed down to the lowest supported versions but those can be bumped locally. For each supported proto version there is a separate `MODULE.proto\u003cversion\u003e.bazel` file that contains the minimum requirements of the necessary support repos.\n\nCheck [Releases](https://registry.bazel.build/modules/helly25_proto) for details. All that is needed is a `bazel_dep` instruction with the correct version.\n\n```bzl\nbazel_dep(name = \"helly25_proto\", version = \"0.0.0\", repo_name = \"com_helly25_proto\")\n```\n\n# Clone\n\nThe clone was made from Google's [CPP-proto-builder](https://github.com/google/cpp-proto-builder), of which the project lead is the original author and lead for over a decade. That includes in particular the parse_proto components which were invented in their original form around 2012 and used widely throughout Google.\n\n## Parse Proto\n\nThe following files were cloned:\n\n```sh\ncp ../cpp-proto-builder/proto_builder/oss/parse_proto_text.* proto/mbo/proto\ncp ../cpp-proto-builder/proto_builder/oss/parse_proto_text_test.cc proto/mbo/proto\ncp ../cpp-proto-builder/proto_builder/oss/tests/simple_message.proto proto/mbo/proto\npatch \u003cproto/mbo/proto/parse_proto_text.diff\n```\n\nThe diff files are available in the repository history.\n\n## Proto Matchers\n\nThe matchers are part of Google's [CPP-proto-builder](https://github.com/google/cpp-proto-builder), (see above).\nAlternatively this could have been done from:\n\n* [FHIR](https://github.com/google/fhir),\n* [nucleus](https://github.com/google/nucleus),\n* [inazarenko's clone](https://github.com/inazarenko/protobuf-matchers).\n\nThe FHIR sources are stripped and the nucleus sources are older and finally inazarenko's clone was\nmodified to remove other Google dependencies which creates the issue that the GoogleTest docs do not\napply as for instance the regular expression library is different.\n\nThe following files were cloned:\n\n```sh\ncp ../cpp-proto-builder/proto_builder/oss/testing/proto_test_util.* proto/mbo/testing/proto\npatch \u003cproto/mbo/testing/proto_test_util.diff\n```\n\nThe diff files are available in the repository history.\n\nThe include guards were updated and the namespace changed to `testing::proto` which allows to\nimport the whole namespace easily. Further logging was switched directly to\n[Abseil logging](https://abseil.io/docs/cpp/guides/logging) (this was not an option when I wrote\nthe proto Builder or when it was open sourced).\n\nThis clone was established 2023.07.15. The source has since been moved and modified but remains as\nclose to the original source as possible.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelly25%2Fproto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhelly25%2Fproto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelly25%2Fproto/lists"}