1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "generator/internal/client_generator.h"
16 #include "google/cloud/internal/absl_str_cat_quiet.h"
17 #include "absl/memory/memory.h"
18 #include "generator/internal/codegen_utils.h"
19 #include "generator/internal/descriptor_utils.h"
20 #include "generator/internal/predicate_utils.h"
21 #include "generator/internal/printer.h"
22 #include <google/api/client.pb.h>
23 #include <google/protobuf/descriptor.h>
24 
25 namespace google {
26 namespace cloud {
27 namespace generator_internal {
28 
ClientGenerator(google::protobuf::ServiceDescriptor const * service_descriptor,VarsDictionary service_vars,std::map<std::string,VarsDictionary> service_method_vars,google::protobuf::compiler::GeneratorContext * context)29 ClientGenerator::ClientGenerator(
30     google::protobuf::ServiceDescriptor const* service_descriptor,
31     VarsDictionary service_vars,
32     std::map<std::string, VarsDictionary> service_method_vars,
33     google::protobuf::compiler::GeneratorContext* context)
34     : ServiceCodeGenerator("client_header_path", "client_cc_path",
35                            service_descriptor, std::move(service_vars),
36                            std::move(service_method_vars), context) {}
37 
GenerateHeader()38 Status ClientGenerator::GenerateHeader() {
39   HeaderPrint(CopyrightLicenseFileHeader());
40   HeaderPrint(  // clang-format off
41     "// Generated by the Codegen C++ plugin.\n"
42     "// If you make any local changes, they will be lost.\n"
43     "// source: $proto_file_name$\n"
44     "#ifndef $header_include_guard$\n"
45     "#define $header_include_guard$\n"
46     "\n");
47   // clang-format on
48 
49   // includes
50   HeaderLocalIncludes({vars("connection_header_path"), "google/cloud/future.h",
51                        "google/cloud/polling_policy.h",
52                        "google/cloud/status_or.h", "google/cloud/version.h"});
53   HeaderSystemIncludes({"google/longrunning/operations.grpc.pb.h", "memory"});
54   HeaderPrint("\n");
55 
56   auto result = HeaderOpenNamespaces();
57   if (!result.ok()) return result;
58 
59   // Client Class
60   HeaderPrint(  // clang-format off
61     "$class_comment_block$\n"
62     "class $client_class_name$ {\n"
63     " public:\n"
64     "  explicit $client_class_name$(ConnectionOptions const& options = ConnectionOptions());\n"
65     "  explicit $client_class_name$(std::shared_ptr<$connection_class_name$> connection);\n"
66     "  ~$client_class_name$();\n"
67     "\n"
68     "  //@{\n"
69     "  // @name Copy and move support\n"
70     "  $client_class_name$($client_class_name$ const&) = default;\n"
71     "  $client_class_name$& operator=($client_class_name$ const&) = default;\n"
72     "  $client_class_name$($client_class_name$&&) noexcept = default;\n"
73     "  $client_class_name$& operator=($client_class_name$&&) noexcept = default;\n"
74     "  //@}\n"
75     "\n"
76     "  //@{\n"
77     "  // @name Equality\n"
78     "  friend bool operator==($client_class_name$ const& a, $client_class_name$ const& b) {\n"
79     "    return a.connection_ == b.connection_;\n"
80     "  }\n"
81     "  friend bool operator!=($client_class_name$ const& a, $client_class_name$ const& b) {\n"
82     "    return !(a == b);\n"
83     "  }\n"
84     "  //@}\n"
85     "\n");
86   // clang-format on
87 
88   for (google::protobuf::MethodDescriptor const& method : methods()) {
89     auto method_signature_extension =
90         method.options().GetRepeatedExtension(google::api::method_signature);
91     for (int i = 0; i < method_signature_extension.size(); ++i) {
92       std::string method_string =
93           absl::StrCat("  $method_name$($method_signature", i, "$);\n\n");
94       HeaderPrintMethod(
95           method,
96           {MethodPattern(
97                {
98                    {"  $method_signature_comment_block$\n"},
99                    {IsResponseTypeEmpty,
100                     // clang-format off
101                    "  Status\n",
102                    "  StatusOr<$response_type$>\n"},
103                   {method_string}
104                    // clang-format on
105                },
106                All(IsNonStreaming, Not(IsLongrunningOperation),
107                    Not(IsPaginated))),
108            MethodPattern(
109                {
110                    {"  $method_signature_comment_block$\n"},
111                    {IsResponseTypeEmpty,
112                     // clang-format off
113                     "  future<Status>\n",
114                     "  future<StatusOr<$longrunning_deduced_response_type$>>\n"},
115                    {method_string}
116                    // clang-format on
117                },
118                All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
119            MethodPattern(
120                {
121                    // clang-format off
122                    {"  $method_signature_comment_block$\n"
123                     "  $method_name$Range\n"},
124                    {method_string},
125                    // clang-format on
126                },
127                All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
128           __FILE__, __LINE__);
129     }
130   }
131 
132   for (auto const& method : methods()) {
133     HeaderPrintMethod(
134         method,
135         {MethodPattern(
136              {
137                  {"  $request_comment_block$\n"},
138                  {IsResponseTypeEmpty,
139                   // clang-format off
140     "  Status\n",
141     "  StatusOr<$response_type$>\n"},
142    {"  $method_name$($request_type$ const& request);\n"
143         "\n"}
144                  // clang-format on
145              },
146              All(IsNonStreaming, Not(IsLongrunningOperation),
147                  Not(IsPaginated))),
148          MethodPattern(
149              {
150                  {"  $request_comment_block$\n"},
151                  {IsResponseTypeEmpty,
152                   // clang-format off
153     "  future<Status>\n",
154     "  future<StatusOr<$longrunning_deduced_response_type$>>\n"},
155    {"  $method_name$($request_type$ const& request);\n"
156         "\n"}
157                  // clang-format on
158              },
159              All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
160          MethodPattern(
161              {
162                  // clang-format off
163                   {"  $request_comment_block$\n"},
164    {"  $method_name$Range\n"
165     "  $method_name$($request_type$ request);\n\n"},
166                  // clang-format on
167              },
168              All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
169         __FILE__, __LINE__);
170   }
171 
172   HeaderPrint(  // clang-format off
173     " private:\n"
174     "  std::shared_ptr<$connection_class_name$> connection_;\n");
175   // clang-format on
176 
177   // close Client class
178   HeaderPrint(  // clang-format off
179     "};\n\n");
180   // clang-format on
181 
182   HeaderCloseNamespaces();
183   // close header guard
184   HeaderPrint(  // clang-format off
185     "#endif  // $header_include_guard$\n");
186   // clang-format on
187   return {};
188 }
189 
GenerateCc()190 Status ClientGenerator::GenerateCc() {
191   CcPrint(CopyrightLicenseFileHeader());
192   CcPrint(  // clang-format off
193     "// Generated by the Codegen C++ plugin.\n"
194     "// If you make any local changes, they will be lost.\n"
195     "// source: $proto_file_name$\n\n");
196   // clang-format on
197 
198   // includes
199   CcLocalIncludes({vars("client_header_path")});
200   CcSystemIncludes({"memory"});
201   CcPrint("\n");
202 
203   auto result = CcOpenNamespaces();
204   if (!result.ok()) return result;
205 
206   CcPrint(  // clang-format off
207     "$client_class_name$::$client_class_name$(ConnectionOptions const& options)\n"
208     " : connection_(Make$connection_class_name$(options)) {}\n");
209   // clang-format on
210 
211   CcPrint(  // clang-format off
212     "$client_class_name$::$client_class_name$(std::shared_ptr<$connection_class_name$> connection)"
213     " : connection_(std::move(connection)) {}\n");
214   // clang-format on
215 
216   CcPrint(  // clang-format off
217     "$client_class_name$::~$client_class_name$() = default;\n\n");
218   // clang-format on
219 
220   for (google::protobuf::MethodDescriptor const& method : methods()) {
221     auto method_signature_extension =
222         method.options().GetRepeatedExtension(google::api::method_signature);
223     for (int i = 0; i < method_signature_extension.size(); ++i) {
224       std::string method_string = absl::StrCat(
225           "$client_class_name$::$method_name$($method_signature", i, "$) {\n");
226       std::string method_request_string =
227           absl::StrCat("$method_request_setters", i, "$");
228       CcPrintMethod(
229           method,
230           {MethodPattern(
231                {
232                    {IsResponseTypeEmpty,
233                     // clang-format off
234                    "Status\n",
235                    "StatusOr<$response_type$>\n"},
236                   {method_string},
237                   {"  $request_type$ request;\n"},
238                    {method_request_string},
239                   {"  return connection_->$method_name$(request);\n"
240                    "}\n\n"}
241                    // clang-format on
242                },
243                All(IsNonStreaming, Not(IsLongrunningOperation),
244                    Not(IsPaginated))),
245            MethodPattern(
246                {
247                    {IsResponseTypeEmpty,
248                     // clang-format off
249                     "future<Status>\n",
250                     "future<StatusOr<$longrunning_deduced_response_type$>>\n"},
251                   {method_string},
252                   {"  $request_type$ request;\n"},
253                    {method_request_string},
254                   {"  return connection_->$method_name$(request);\n"
255                   "}\n\n"}
256                    // clang-format on
257                },
258                All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
259            MethodPattern(
260                {
261                    // clang-format off
262                    {"$method_name$Range\n"},
263                   {method_string},
264                   {"  $request_type$ request;\n"},
265                    {method_request_string},
266                   {"  return connection_->$method_name$(request);\n"
267                   "}\n\n"}
268                    // clang-format on
269                },
270                All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
271           __FILE__, __LINE__);
272     }
273   }
274 
275   for (auto const& method : methods()) {
276     CcPrintMethod(
277         method,
278         {MethodPattern(
279              {
280                  {IsResponseTypeEmpty,
281                   // clang-format off
282     "Status\n",
283     "StatusOr<$response_type$>\n"},
284    {"$client_class_name$::$method_name$($request_type$ const& request) {\n"
285     "  return connection_->$method_name$(request);\n"
286     "}\n\n"}
287                  // clang-format on
288              },
289              All(IsNonStreaming, Not(IsLongrunningOperation),
290                  Not(IsPaginated))),
291          MethodPattern(
292              {
293                  {IsResponseTypeEmpty,
294                   // clang-format off
295     "future<Status>\n",
296     "future<StatusOr<$longrunning_deduced_response_type$>>\n"},
297    {"$client_class_name$::$method_name$($request_type$ const& request) {\n"
298     "  return connection_->$method_name$(request);\n"
299     "}\n\n"}
300                  // clang-format on
301              },
302              All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
303          MethodPattern(
304              {
305                  // clang-format off
306    {"$method_name$Range\n"
307     "$client_class_name$::$method_name$($request_type$ request) {\n"
308     "  return connection_->$method_name$(request);\n"
309     "}\n\n"}
310                  // clang-format on
311              },
312              All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
313         __FILE__, __LINE__);
314   }
315 
316   CcCloseNamespaces();
317   return {};
318 }
319 
320 }  // namespace generator_internal
321 }  // namespace cloud
322 }  // namespace google
323