xref: /aosp_15_r20/external/grpc-grpc/src/compiler/generator_helpers.h (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
20 #define GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
21 
22 #include <iostream>
23 #include <map>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
28 #include "src/compiler/config.h"
29 #include "src/compiler/proto_parser_helper.h"
30 
31 namespace grpc_generator {
32 
StripSuffix(std::string * filename,const std::string & suffix)33 inline bool StripSuffix(std::string* filename, const std::string& suffix) {
34   if (filename->length() >= suffix.length()) {
35     size_t suffix_pos = filename->length() - suffix.length();
36     if (filename->compare(suffix_pos, std::string::npos, suffix) == 0) {
37       filename->resize(filename->size() - suffix.size());
38       return true;
39     }
40   }
41 
42   return false;
43 }
44 
StripPrefix(std::string * name,const std::string & prefix)45 inline bool StripPrefix(std::string* name, const std::string& prefix) {
46   if (name->length() >= prefix.length()) {
47     if (name->substr(0, prefix.size()) == prefix) {
48       *name = name->substr(prefix.size());
49       return true;
50     }
51   }
52   return false;
53 }
54 
StripProto(std::string filename)55 inline std::string StripProto(std::string filename) {
56   if (!StripSuffix(&filename, ".protodevel")) {
57     StripSuffix(&filename, ".proto");
58   }
59   return filename;
60 }
61 
StringReplace(std::string str,const std::string & from,const std::string & to,bool replace_all)62 inline std::string StringReplace(std::string str, const std::string& from,
63                                  const std::string& to, bool replace_all) {
64   size_t pos = 0;
65 
66   do {
67     pos = str.find(from, pos);
68     if (pos == std::string::npos) {
69       break;
70     }
71     str.replace(pos, from.length(), to);
72     pos += to.length();
73   } while (replace_all);
74 
75   return str;
76 }
77 
StringReplace(std::string str,const std::string & from,const std::string & to)78 inline std::string StringReplace(std::string str, const std::string& from,
79                                  const std::string& to) {
80   return StringReplace(str, from, to, true);
81 }
82 
tokenize(const std::string & input,const std::string & delimiters)83 inline std::vector<std::string> tokenize(const std::string& input,
84                                          const std::string& delimiters) {
85   std::vector<std::string> tokens;
86   size_t pos, last_pos = 0;
87 
88   for (;;) {
89     bool done = false;
90     pos = input.find_first_of(delimiters, last_pos);
91     if (pos == std::string::npos) {
92       done = true;
93       pos = input.length();
94     }
95 
96     tokens.push_back(input.substr(last_pos, pos - last_pos));
97     if (done) return tokens;
98 
99     last_pos = pos + 1;
100   }
101 }
102 
CapitalizeFirstLetter(std::string s)103 inline std::string CapitalizeFirstLetter(std::string s) {
104   if (s.empty()) {
105     return s;
106   }
107   s[0] = ::toupper(s[0]);
108   return s;
109 }
110 
LowercaseFirstLetter(std::string s)111 inline std::string LowercaseFirstLetter(std::string s) {
112   if (s.empty()) {
113     return s;
114   }
115   s[0] = ::tolower(s[0]);
116   return s;
117 }
118 
LowerUnderscoreToUpperCamel(std::string str)119 inline std::string LowerUnderscoreToUpperCamel(std::string str) {
120   std::vector<std::string> tokens = tokenize(str, "_");
121   std::string result = "";
122   for (unsigned int i = 0; i < tokens.size(); i++) {
123     result += CapitalizeFirstLetter(tokens[i]);
124   }
125   return result;
126 }
127 
FileNameInUpperCamel(const grpc::protobuf::FileDescriptor * file,bool include_package_path)128 inline std::string FileNameInUpperCamel(
129     const grpc::protobuf::FileDescriptor* file, bool include_package_path) {
130   std::vector<std::string> tokens = tokenize(StripProto(file->name()), "/");
131   std::string result = "";
132   if (include_package_path) {
133     for (unsigned int i = 0; i < tokens.size() - 1; i++) {
134       result += tokens[i] + "/";
135     }
136   }
137   result += LowerUnderscoreToUpperCamel(tokens.back());
138   return result;
139 }
140 
FileNameInUpperCamel(const grpc::protobuf::FileDescriptor * file)141 inline std::string FileNameInUpperCamel(
142     const grpc::protobuf::FileDescriptor* file) {
143   return FileNameInUpperCamel(file, true);
144 }
145 
146 enum MethodType {
147   METHODTYPE_NO_STREAMING,
148   METHODTYPE_CLIENT_STREAMING,
149   METHODTYPE_SERVER_STREAMING,
150   METHODTYPE_BIDI_STREAMING
151 };
152 
GetMethodType(const grpc::protobuf::MethodDescriptor * method)153 inline MethodType GetMethodType(
154     const grpc::protobuf::MethodDescriptor* method) {
155   if (method->client_streaming()) {
156     if (method->server_streaming()) {
157       return METHODTYPE_BIDI_STREAMING;
158     } else {
159       return METHODTYPE_CLIENT_STREAMING;
160     }
161   } else {
162     if (method->server_streaming()) {
163       return METHODTYPE_SERVER_STREAMING;
164     } else {
165       return METHODTYPE_NO_STREAMING;
166     }
167   }
168 }
169 
Split(const std::string & s,char,std::vector<std::string> * append_to)170 inline void Split(const std::string& s, char /*delim*/,
171                   std::vector<std::string>* append_to) {
172   std::istringstream iss(s);
173   std::string piece;
174   while (std::getline(iss, piece)) {
175     append_to->push_back(piece);
176   }
177 }
178 
179 enum CommentType {
180   COMMENTTYPE_LEADING,
181   COMMENTTYPE_TRAILING,
182   COMMENTTYPE_LEADING_DETACHED
183 };
184 
185 // Get all the raw comments and append each line without newline to out.
186 template <typename DescriptorType>
GetComment(const DescriptorType * desc,CommentType type,std::vector<std::string> * out)187 inline void GetComment(const DescriptorType* desc, CommentType type,
188                        std::vector<std::string>* out) {
189   grpc::protobuf::SourceLocation location;
190   if (!desc->GetSourceLocation(&location)) {
191     return;
192   }
193   if (type == COMMENTTYPE_LEADING || type == COMMENTTYPE_TRAILING) {
194     const std::string& comments = type == COMMENTTYPE_LEADING
195                                       ? location.leading_comments
196                                       : location.trailing_comments;
197     Split(comments, '\n', out);
198   } else if (type == COMMENTTYPE_LEADING_DETACHED) {
199     for (unsigned int i = 0; i < location.leading_detached_comments.size();
200          i++) {
201       Split(location.leading_detached_comments[i], '\n', out);
202       out->push_back("");
203     }
204   } else {
205     std::cerr << "Unknown comment type " << type << std::endl;
206     abort();
207   }
208 }
209 
210 // Each raw comment line without newline is appended to out.
211 // For file level leading and detached leading comments, we return comments
212 // above syntax line. Return nothing for trailing comments.
213 template <>
GetComment(const grpc::protobuf::FileDescriptor * desc,CommentType type,std::vector<std::string> * out)214 inline void GetComment(const grpc::protobuf::FileDescriptor* desc,
215                        CommentType type, std::vector<std::string>* out) {
216   if (type == COMMENTTYPE_TRAILING) {
217     return;
218   }
219   grpc::protobuf::SourceLocation location;
220   std::vector<int> path;
221   path.push_back(grpc::protobuf::FileDescriptorProto::kSyntaxFieldNumber);
222   if (!desc->GetSourceLocation(path, &location)) {
223     return;
224   }
225   if (type == COMMENTTYPE_LEADING) {
226     Split(location.leading_comments, '\n', out);
227   } else if (type == COMMENTTYPE_LEADING_DETACHED) {
228     for (unsigned int i = 0; i < location.leading_detached_comments.size();
229          i++) {
230       Split(location.leading_detached_comments[i], '\n', out);
231       out->push_back("");
232     }
233   } else {
234     std::cerr << "Unknown comment type " << type << std::endl;
235     abort();
236   }
237 }
238 
239 // Add prefix and newline to each comment line and concatenate them together.
240 // Make sure there is a space after the prefix unless the line is empty.
GenerateCommentsWithPrefix(const std::vector<std::string> & in,const std::string & prefix)241 inline std::string GenerateCommentsWithPrefix(
242     const std::vector<std::string>& in, const std::string& prefix) {
243   std::ostringstream oss;
244   for (auto it = in.begin(); it != in.end(); it++) {
245     const std::string& elem = *it;
246     if (elem.empty()) {
247       oss << prefix << "\n";
248     } else if (elem[0] == ' ') {
249       oss << prefix << EscapeVariableDelimiters(elem) << "\n";
250     } else {
251       oss << prefix << " " << EscapeVariableDelimiters(elem) << "\n";
252     }
253   }
254   return oss.str();
255 }
256 
257 template <typename DescriptorType>
GetPrefixedComments(const DescriptorType * desc,bool leading,const std::string & prefix)258 inline std::string GetPrefixedComments(const DescriptorType* desc, bool leading,
259                                        const std::string& prefix) {
260   std::vector<std::string> out;
261   if (leading) {
262     grpc_generator::GetComment(
263         desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &out);
264     std::vector<std::string> leading;
265     grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
266                                &leading);
267     out.insert(out.end(), leading.begin(), leading.end());
268   } else {
269     grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
270                                &out);
271   }
272   return GenerateCommentsWithPrefix(out, prefix);
273 }
274 
275 }  // namespace grpc_generator
276 
277 #endif  // GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
278