1.. _docs-pw-style-protobuf: 2 3============== 4Protobuf style 5============== 6The Pigweed protobuf style guide is closely based on `Google's public 7protobuf style guide <https://protobuf.dev/programming-guides/style/>`_. 8The Google Protobuf Style Guide applies to Pigweed except as described in this 9document. 10 11The Pigweed protobuf style guide only applies to Pigweed itself. It does not 12apply to projects that use Pigweed or to the third-party code included with 13Pigweed. 14 15-------- 16Editions 17-------- 18Pigweed only allows ``proto3`` (i.e. ``syntax = "proto3";``) at this time. 19``proto2`` is not supported, and the ``edition`` versions (e.g. 20``edition = "2023";``) will be considered when sufficient motivation arises. 21 22------ 23Naming 24------ 25Protobuf library structure 26========================== 27All protobuf files should live in a ``proto`` subdirectory within their 28respective modules. This produces an ``import`` path that matches the pattern of 29``[module name]/proto/[proto file].proto``\. If a module exposes multiple proto 30libraries that need to be grouped separately from a build/distribution 31perspective, additional directories may be introduced that follow the pattern 32``[module name]/[group name]_proto/[proto file].proto``\. 33 34Examples: 35 36* ``pw_log/proto`` 37* ``pw_unit_test/proto`` 38* ``pw_snapshot/proto`` 39* ``pw_snapshot/metadata_proto`` 40 41Rationale: This overlays the include paths of native libraries, but introduces 42``proto`` to the enclosing directory hierarchy to make it clearer that generated 43proto libraries are being introduced. 44 45Protobuf package names 46====================== 47Protobuf library packages should append a ``proto`` suffix to package 48namespaces. If a module exposes multiple proto 49libraries that need to be grouped separately from a build/distribution 50perspective, a ``*_proto`` suffix may be used instead. 51 52Example: 53 54.. code-block:: protobuf 55 :emphasize-lines: 3 56 57 // This lives inside of pw_file. 58 59 package pw.file.proto; 60 61 service FileSystem { 62 // ... 63 } 64 65Rationale: This prevents collisions between generated proto types and 66language-native types. 67 68RPC service names 69================= 70Services should strive for simple, intuitive, globally unique names, and should 71**not** be suffixed with ``*Service``. 72 73Example: 74 75.. code-block:: protobuf 76 :emphasize-lines: 1 77 78 service PwEcho { 79 rpc Echo(EchoRequest) returns (EchoResponse) {} 80 } 81 82Rationale: Pigweed's C++ RPC codegen namespaces services differently than 83regular protos (for example, ``pw::file::pw_rpc::raw::FileSystem::Service``), 84which makes it sufficiently clear when a name refers to a service. 85 86------ 87Typing 88------ 89Using ``optional`` 90================== 91When presence or absence of a field's value has a semantic meaning, the field 92should be marked as ``optional`` to signal the distinction. Otherwise, 93``optional`` should be omitted. This causes the field to still be optional, but 94indicates that the default value of zero is semantically equivalent to an 95explicit value of zero. 96 97Example: 98 99.. code-block:: protobuf 100 :emphasize-lines: 6 101 102 message MyRequest { 103 // The maximum number of `foo` entries to return in the response message. 104 // If this is not set, the response may contain as many `foo` entries 105 // as needed. If `max_foo` is zero, no `foo` entries should be included 106 // in the response. 107 optional uint32 max_foo = 1; 108 } 109 110Rationale: ``optional`` is very useful for clarifying cases where an implied 111default value seems ambiguous, and it also signals codegen for ``has_max_foo`` 112getters in various languages. 113 114Using ``required`` 115================== 116``required`` fields are strictly forbidden within Pigweed. 117 118----- 119Enums 120----- 121Default values 122============== 123All enums must have a safe default zero value. Usually this is ``UNKNOWN`` or 124``NONE``, but may be any other semantically similar default. 125 126Rationale: Enums are default-initialized to zero, which means that a zero value 127that conveys anything beyond "unset" may be misleading. 128 129.. code-block:: protobuf 130 :emphasize-lines: 5 131 132 package pw.chrono.proto; 133 134 message EpochType { 135 enum Enum { 136 UNKNOWN = 0; 137 TIME_SINCE_BOOT = 1; 138 UTC_WALL_CLOCK = 2; 139 GPS_WALL_CLOCK = 3; 140 TAI_WALL_CLOCK = 4; 141 }; 142 } 143 144Namespacing 145=========== 146Prefer to place ``enum`` definitions within a ``message`` to namespace the 147generated names for the values. 148 149Rationale: Enum value names can easily collide if you don't prefix them (i.e. 150``UNKNOWN`` from one enum will collide with ``UNKNOWN`` from another enum). 151Namespacing them within a message prevents these collisions. 152 153.. code-block:: protobuf 154 :emphasize-lines: 3, 4 155 156 package pw.chrono.proto; 157 158 message EpochType { 159 enum Enum { 160 UNKNOWN = 0; 161 TIME_SINCE_BOOT = 1; 162 UTC_WALL_CLOCK = 2; 163 GPS_WALL_CLOCK = 3; 164 TAI_WALL_CLOCK = 4; 165 }; 166 } 167