pw_protobuf_compiler: Iterate over messages

Support iterating over all message types in a proto Library.

Change-Id: Ia95e30842c4ea8a9795762ebb3cf9a7a27597207
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/32881
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
diff --git a/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py b/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
index 0c4a8cc..9b6f6cd 100644
--- a/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
+++ b/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
@@ -308,10 +308,23 @@
             for m in modules)
 
     def modules(self) -> Iterable:
-        """Allows iterating over all protobuf modules in this library."""
+        """Iterates over all protobuf modules in this library."""
         for module_list in self.modules_by_package.values():
             yield from module_list
 
+    def messages(self) -> Iterable:
+        """Iterates over all protobuf messages in this library."""
+        for module in self.modules():
+            yield from _nested_messages(
+                module, module.DESCRIPTOR.message_types_by_name)
+
+
+def _nested_messages(scope, message_names: Iterable[str]) -> Iterator:
+    for name in message_names:
+        msg = getattr(scope, name)
+        yield msg
+        yield from _nested_messages(msg, msg.DESCRIPTOR.nested_types_by_name)
+
 
 def _repr_char(char: int) -> str:
     r"""Returns an ASCII char or the \x code for non-printable values."""
diff --git a/pw_protobuf_compiler/py/python_protos_test.py b/pw_protobuf_compiler/py/python_protos_test.py
index 82ed547..7888c55 100755
--- a/pw_protobuf_compiler/py/python_protos_test.py
+++ b/pw_protobuf_compiler/py/python_protos_test.py
@@ -84,6 +84,18 @@
   repeated int64 value = 1;
   Greeting hi = 2;
 }
+
+message NestingMessage {
+  message NestedMessage {
+    message NestedNestedMessage {
+      int32 nested_nested_field = 1;
+    }
+
+    NestedNestedMessage nested_nested_message = 1;
+  }
+
+  NestedMessage nested_message = 1;
+}
 """
 
 
@@ -201,6 +213,20 @@
         with self.assertRaises(KeyError):
             _ = self._library.packages.pw.protobuf_compiler['not here']
 
+    def test_messages(self):
+        protos = self._library.packages.pw.protobuf_compiler
+        self.assertEqual(
+            set(self._library.messages()), {
+                protos.test1.SomeMessage,
+                protos.test1.AnotherMessage,
+                protos.test2.Request,
+                protos.test2.Response,
+                protos.test2.Hello,
+                protos.test2.NestingMessage,
+                protos.test2.NestingMessage.NestedMessage,
+                protos.test2.NestingMessage.NestedMessage.NestedNestedMessage,
+            })
+
 
 PROTO_FOR_REPR = """\
 syntax = "proto3";