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/service_code_generator.h"
16 #include "google/cloud/internal/absl_str_cat_quiet.h"
17 #include "google/cloud/internal/absl_str_replace_quiet.h"
18 #include "google/cloud/log.h"
19 #include "absl/memory/memory.h"
20 #include "absl/strings/str_split.h"
21 #include "absl/strings/strip.h"
22 #include "generator/internal/codegen_utils.h"
23 #include "generator/internal/printer.h"
24 #include <google/api/client.pb.h>
25 #include <google/protobuf/descriptor.h>
26 
27 namespace google {
28 namespace cloud {
29 namespace generator_internal {
30 
ServiceCodeGenerator(std::string const & header_path_key,std::string const & cc_path_key,google::protobuf::ServiceDescriptor const * service_descriptor,VarsDictionary service_vars,std::map<std::string,VarsDictionary> service_method_vars,google::protobuf::compiler::GeneratorContext * context)31 ServiceCodeGenerator::ServiceCodeGenerator(
32     std::string const& header_path_key, std::string const& cc_path_key,
33     google::protobuf::ServiceDescriptor const* service_descriptor,
34     VarsDictionary service_vars,
35     std::map<std::string, VarsDictionary> service_method_vars,
36     google::protobuf::compiler::GeneratorContext* context)
37     : service_descriptor_(service_descriptor),
38       service_vars_(std::move(service_vars)),
39       service_method_vars_(std::move(service_method_vars)),
40       header_(context, service_vars_[header_path_key]),
41       cc_(context, service_vars_[cc_path_key]) {
42   assert(service_descriptor != nullptr);
43   assert(context != nullptr);
44   SetVars(service_vars_[header_path_key]);
45   for (int i = 0; i < service_descriptor_->method_count(); ++i) {
46     methods_.emplace_back(*service_descriptor_->method(i));
47   }
48 }
49 
ServiceCodeGenerator(std::string const & header_path_key,google::protobuf::ServiceDescriptor const * service_descriptor,VarsDictionary service_vars,std::map<std::string,VarsDictionary> service_method_vars,google::protobuf::compiler::GeneratorContext * context)50 ServiceCodeGenerator::ServiceCodeGenerator(
51     std::string const& header_path_key,
52     google::protobuf::ServiceDescriptor const* service_descriptor,
53     VarsDictionary service_vars,
54     std::map<std::string, VarsDictionary> service_method_vars,
55     google::protobuf::compiler::GeneratorContext* context)
56     : service_descriptor_(service_descriptor),
57       service_vars_(std::move(service_vars)),
58       service_method_vars_(std::move(service_method_vars)),
59       header_(context, service_vars_[header_path_key]) {
60   assert(service_descriptor != nullptr);
61   assert(context != nullptr);
62   SetVars(service_vars_[header_path_key]);
63   for (int i = 0; i < service_descriptor_->method_count(); ++i) {
64     methods_.emplace_back(*service_descriptor_->method(i));
65   }
66 }
67 
vars() const68 VarsDictionary const& ServiceCodeGenerator::vars() const {
69   return service_vars_;
70 }
71 
vars(std::string const & key) const72 std::string ServiceCodeGenerator::vars(std::string const& key) const {
73   auto iter = service_vars_.find(key);
74   if (iter == service_vars_.end()) {
75     GCP_LOG(FATAL) << key << " not found in service_vars_\n";
76   }
77   return iter->second;
78 }
79 
80 std::vector<
81     std::reference_wrapper<google::protobuf::MethodDescriptor const>> const&
methods() const82 ServiceCodeGenerator::methods() const {
83   return methods_;
84 }
85 
MergeServiceAndMethodVars(google::protobuf::MethodDescriptor const & method) const86 VarsDictionary ServiceCodeGenerator::MergeServiceAndMethodVars(
87     google::protobuf::MethodDescriptor const& method) const {
88   auto vars = service_vars_;
89   vars.insert(service_method_vars_.at(method.full_name()).begin(),
90               service_method_vars_.at(method.full_name()).end());
91   return vars;
92 }
93 
HeaderLocalIncludes(std::vector<std::string> const & local_includes)94 void ServiceCodeGenerator::HeaderLocalIncludes(
95     std::vector<std::string> const& local_includes) {
96   GenerateLocalIncludes(header_, local_includes);
97 }
98 
CcLocalIncludes(std::vector<std::string> const & local_includes)99 void ServiceCodeGenerator::CcLocalIncludes(
100     std::vector<std::string> const& local_includes) {
101   GenerateLocalIncludes(cc_, local_includes, FileType::kCcFile);
102 }
103 
HeaderSystemIncludes(std::vector<std::string> const & system_includes)104 void ServiceCodeGenerator::HeaderSystemIncludes(
105     std::vector<std::string> const& system_includes) {
106   GenerateSystemIncludes(header_, system_includes);
107 }
108 
CcSystemIncludes(std::vector<std::string> const & system_includes)109 void ServiceCodeGenerator::CcSystemIncludes(
110     std::vector<std::string> const& system_includes) {
111   GenerateSystemIncludes(cc_, system_includes);
112 }
113 
HeaderOpenNamespaces(NamespaceType ns_type)114 Status ServiceCodeGenerator::HeaderOpenNamespaces(NamespaceType ns_type) {
115   return OpenNamespaces(header_, ns_type);
116 }
117 
HeaderCloseNamespaces()118 void ServiceCodeGenerator::HeaderCloseNamespaces() { CloseNamespaces(header_); }
119 
CcOpenNamespaces(NamespaceType ns_type)120 Status ServiceCodeGenerator::CcOpenNamespaces(NamespaceType ns_type) {
121   return OpenNamespaces(cc_, ns_type);
122 }
123 
CcCloseNamespaces()124 void ServiceCodeGenerator::CcCloseNamespaces() { CloseNamespaces(cc_); }
125 
HeaderPrint(std::string const & text)126 void ServiceCodeGenerator::HeaderPrint(std::string const& text) {
127   header_.Print(service_vars_, text);
128 }
129 
HeaderPrintMethod(google::protobuf::MethodDescriptor const & method,std::vector<MethodPattern> const & patterns,char const * file,int line)130 Status ServiceCodeGenerator::HeaderPrintMethod(
131     google::protobuf::MethodDescriptor const& method,
132     std::vector<MethodPattern> const& patterns, char const* file, int line) {
133   return PrintMethod(method, header_, MergeServiceAndMethodVars(method),
134                      patterns, file, line);
135 }
136 
CcPrint(std::string const & text)137 void ServiceCodeGenerator::CcPrint(std::string const& text) {
138   cc_.Print(service_vars_, text);
139 }
140 
CcPrintMethod(google::protobuf::MethodDescriptor const & method,std::vector<MethodPattern> const & patterns,char const * file,int line)141 Status ServiceCodeGenerator::CcPrintMethod(
142     google::protobuf::MethodDescriptor const& method,
143     std::vector<MethodPattern> const& patterns, char const* file, int line) {
144   return PrintMethod(method, cc_, MergeServiceAndMethodVars(method), patterns,
145                      file, line);
146 }
147 
GenerateLocalIncludes(Printer & p,std::vector<std::string> local_includes,FileType file_type)148 void ServiceCodeGenerator::GenerateLocalIncludes(
149     Printer& p, std::vector<std::string> local_includes, FileType file_type) {
150   if (file_type == FileType::kCcFile) {
151     std::sort(local_includes.begin() + 1, local_includes.end());
152   } else {
153     std::sort(local_includes.begin(), local_includes.end());
154   }
155   for (auto const& include : local_includes) {
156     p.Print(LocalInclude(include));
157   }
158 }
159 
GenerateSystemIncludes(Printer & p,std::vector<std::string> system_includes)160 void ServiceCodeGenerator::GenerateSystemIncludes(
161     Printer& p, std::vector<std::string> system_includes) {
162   std::sort(system_includes.begin(), system_includes.end());
163   for (auto const& include : system_includes) {
164     p.Print(SystemInclude(include));
165   }
166 }
167 
OpenNamespaces(Printer & p,NamespaceType ns_type)168 Status ServiceCodeGenerator::OpenNamespaces(Printer& p, NamespaceType ns_type) {
169   auto result = service_vars_.find("product_path");
170   if (result == service_vars_.end()) {
171     return Status(StatusCode::kInternal, "product_path not found in vars");
172   }
173   namespaces_ = BuildNamespaces(service_vars_["product_path"], ns_type);
174   for (auto const& nspace : namespaces_) {
175     if (nspace == "GOOGLE_CLOUD_CPP_NS") {
176       p.Print("inline namespace $namespace$ {\n", "namespace", nspace);
177     } else {
178       p.Print("namespace $namespace$ {\n", "namespace", nspace);
179     }
180   }
181   p.Print("\n");
182   return {};
183 }
184 
CloseNamespaces(Printer & p)185 void ServiceCodeGenerator::CloseNamespaces(Printer& p) {
186   for (auto iter = namespaces_.rbegin(); iter != namespaces_.rend(); ++iter) {
187     p.Print("}  // namespace $namespace$\n", "namespace", *iter);
188   }
189   p.Print("\n");
190 }
191 
Generate()192 Status ServiceCodeGenerator::Generate() {
193   auto result = GenerateHeader();
194   if (!result.ok()) return result;
195   return GenerateCc();
196 }
197 
SetVars(absl::string_view header_path)198 void ServiceCodeGenerator::SetVars(absl::string_view header_path) {
199   service_vars_["header_include_guard"] = absl::StrCat(
200       "GOOGLE_CLOUD_CPP_", absl::AsciiStrToUpper(absl::StrReplaceAll(
201                                header_path, {{"/", "_"}, {".", "_"}})));
202 }
203 
204 }  // namespace generator_internal
205 }  // namespace cloud
206 }  // namespace google
207