pw_rpc: Generate aliases for nanopb client call types

Nanopb client calls are templated and can have long type names. For
example, the client call type for the nanopb EchoService.Echo method is

  NanopbClientCall<UnaryResponseHandler<pw_rpc_EchoMessage>>

This updates the generated RPC client code to provide aliases for the
client call types of each method within the service client. The above
type can now be replaced with

  EchoServiceClient::EchoCall

which is much more legible.

Change-Id: I184e34b2b52b120b83227be82c4a411dcd4941ec
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/42929
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst
index 9518682..3377b26 100644
--- a/pw_rpc/docs.rst
+++ b/pw_rpc/docs.rst
@@ -875,8 +875,10 @@
     }
   };
 
-  pw::rpc::NanopbClientCall<pw::rpc::UnaryResponseHandler<pw_rpc_EchoMessage>>
-      echo_call;
+  // Generated clients are namespaced with their proto library.
+  using pw::rpc::nanopb::EchoServiceClient;
+
+  EchoServiceClient::EchoCall echo_call;
   EchoResponseHandler response_handler;
 
   }  // namespace
@@ -888,8 +890,7 @@
     // By assigning the returned ClientCall to the global echo_call, the RPC
     // call is kept alive until it completes. When a response is received, it
     // will be logged by the handler function and the call will complete.
-    echo_call = pw::rpc::nanopb::EchoServiceClient::Echo(
-        my_channel, request, response_handler);
+    echo_call = EchoServiceClient::Echo(my_channel, request, response_handler);
   }
 
 Client implementation details
diff --git a/pw_rpc/nanopb/codegen_test.cc b/pw_rpc/nanopb/codegen_test.cc
index 5b7241a..aae7b00 100644
--- a/pw_rpc/nanopb/codegen_test.cc
+++ b/pw_rpc/nanopb/codegen_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Pigweed Authors
+// Copyright 2021 The Pigweed Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
 // use this file except in compliance with the License. You may obtain a copy of
@@ -134,6 +134,16 @@
 using internal::TestServerStreamingResponseHandler;
 using internal::TestUnaryResponseHandler;
 
+TEST(NanopbCodegen, Client_GeneratesCallAliases) {
+  static_assert(
+      std::is_same_v<
+          TestServiceClient::TestRpcCall,
+          NanopbClientCall<UnaryResponseHandler<pw_rpc_test_TestResponse>>>);
+  static_assert(std::is_same_v<TestServiceClient::TestStreamRpcCall,
+                               NanopbClientCall<ServerStreamingResponseHandler<
+                                   pw_rpc_test_TestStreamResponse>>>);
+}
+
 TEST(NanopbCodegen, Client_InvokesUnaryRpcWithCallback) {
   constexpr uint32_t service_id = internal::Hash("pw.rpc.test.TestService");
   constexpr uint32_t method_id = internal::Hash("TestRpc");
diff --git a/pw_rpc/py/pw_rpc/codegen_nanopb.py b/pw_rpc/py/pw_rpc/codegen_nanopb.py
index 183856c..efb778c 100644
--- a/pw_rpc/py/pw_rpc/codegen_nanopb.py
+++ b/pw_rpc/py/pw_rpc/codegen_nanopb.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Pigweed Authors
+# Copyright 2021 The Pigweed Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -60,7 +60,8 @@
 
 def _generate_server_writer_alias(output: OutputFile) -> None:
     output.write_line('template <typename T>')
-    output.write_line('using ServerWriter = ::pw::rpc::ServerWriter<T>;')
+    output.write_line(
+        f'using ServerWriter = {RPC_NAMESPACE}::ServerWriter<T>;')
 
 
 def _generate_code_for_service(service: ProtoService, root: ProtoNode,
@@ -86,17 +87,22 @@
         raise NotImplementedError(
             'Only unary and server streaming RPCs are currently supported')
 
+    call_alias = f'{method.name()}Call'
+
     output.write_line()
-    output.write_line(f'static NanopbClientCall<\n    {callback}>')
-    output.write_line(f'{method.name()}({RPC_NAMESPACE}::Channel& channel,')
-    with output.indent(len(method.name()) + 1):
+    output.write_line(
+        f'using {call_alias} = {RPC_NAMESPACE}::NanopbClientCall<')
+    output.write_line(f'    {callback}>;')
+    output.write_line()
+    output.write_line(f'static {call_alias} {method.name()}(')
+    with output.indent(4):
+        output.write_line(f'{RPC_NAMESPACE}::Channel& channel,')
         output.write_line(f'const {req}& request,')
         output.write_line(f'{callback}& callback) {{')
 
     with output.indent():
-        output.write_line(f'NanopbClientCall<{callback}>')
-        output.write_line('    call(&channel,')
-        with output.indent(9):
+        output.write_line(f'{call_alias} call(&channel,')
+        with output.indent(len(call_alias) + 6):
             output.write_line('kServiceId,')
             output.write_line(
                 f'0x{method_id:08x},  // Hash of "{method.name()}"')
@@ -120,11 +126,6 @@
     output.write_line(' public:')
 
     with output.indent():
-        output.write_line('template <typename T>')
-        output.write_line(
-            f'using NanopbClientCall = {RPC_NAMESPACE}::NanopbClientCall<T>;')
-
-        output.write_line('')
         output.write_line(f'{class_name}() = delete;')
 
         for method in service.methods():