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 =
131 tokenize(StripProto(std::string(file->name())), "/");
132 std::string result = "";
133 if (include_package_path) {
134 for (unsigned int i = 0; i < tokens.size() - 1; i++) {
135 result += tokens[i] + "/";
136 }
137 }
138 result += LowerUnderscoreToUpperCamel(tokens.back());
139 return result;
140 }
141
FileNameInUpperCamel(const grpc::protobuf::FileDescriptor * file)142 inline std::string FileNameInUpperCamel(
143 const grpc::protobuf::FileDescriptor* file) {
144 return FileNameInUpperCamel(file, true);
145 }
146
147 enum MethodType {
148 METHODTYPE_NO_STREAMING,
149 METHODTYPE_CLIENT_STREAMING,
150 METHODTYPE_SERVER_STREAMING,
151 METHODTYPE_BIDI_STREAMING
152 };
153
GetMethodType(const grpc::protobuf::MethodDescriptor * method)154 inline MethodType GetMethodType(
155 const grpc::protobuf::MethodDescriptor* method) {
156 if (method->client_streaming()) {
157 if (method->server_streaming()) {
158 return METHODTYPE_BIDI_STREAMING;
159 } else {
160 return METHODTYPE_CLIENT_STREAMING;
161 }
162 } else {
163 if (method->server_streaming()) {
164 return METHODTYPE_SERVER_STREAMING;
165 } else {
166 return METHODTYPE_NO_STREAMING;
167 }
168 }
169 }
170
Split(const std::string & s,char,std::vector<std::string> * append_to)171 inline void Split(const std::string& s, char /*delim*/,
172 std::vector<std::string>* append_to) {
173 std::istringstream iss(s);
174 std::string piece;
175 while (std::getline(iss, piece)) {
176 append_to->push_back(piece);
177 }
178 }
179
180 enum CommentType {
181 COMMENTTYPE_LEADING,
182 COMMENTTYPE_TRAILING,
183 COMMENTTYPE_LEADING_DETACHED
184 };
185
186 // Get all the raw comments and append each line without newline to out.
187 template <typename DescriptorType>
GetComment(const DescriptorType * desc,CommentType type,std::vector<std::string> * out)188 inline void GetComment(const DescriptorType* desc, CommentType type,
189 std::vector<std::string>* out) {
190 grpc::protobuf::SourceLocation location;
191 if (!desc->GetSourceLocation(&location)) {
192 return;
193 }
194 if (type == COMMENTTYPE_LEADING || type == COMMENTTYPE_TRAILING) {
195 const std::string& comments = type == COMMENTTYPE_LEADING
196 ? location.leading_comments
197 : location.trailing_comments;
198 Split(comments, '\n', out);
199 } else if (type == COMMENTTYPE_LEADING_DETACHED) {
200 for (unsigned int i = 0; i < location.leading_detached_comments.size();
201 i++) {
202 Split(location.leading_detached_comments[i], '\n', out);
203 out->push_back("");
204 }
205 } else {
206 std::cerr << "Unknown comment type " << type << std::endl;
207 abort();
208 }
209 }
210
211 // Each raw comment line without newline is appended to out.
212 // For file level leading and detached leading comments, we return comments
213 // above syntax line. Return nothing for trailing comments.
214 template <>
GetComment(const grpc::protobuf::FileDescriptor * desc,CommentType type,std::vector<std::string> * out)215 inline void GetComment(const grpc::protobuf::FileDescriptor* desc,
216 CommentType type, std::vector<std::string>* out) {
217 if (type == COMMENTTYPE_TRAILING) {
218 return;
219 }
220 grpc::protobuf::SourceLocation location;
221 std::vector<int> path;
222 path.push_back(grpc::protobuf::FileDescriptorProto::kSyntaxFieldNumber);
223 if (!desc->GetSourceLocation(path, &location)) {
224 return;
225 }
226 if (type == COMMENTTYPE_LEADING) {
227 Split(location.leading_comments, '\n', out);
228 } else if (type == COMMENTTYPE_LEADING_DETACHED) {
229 for (unsigned int i = 0; i < location.leading_detached_comments.size();
230 i++) {
231 Split(location.leading_detached_comments[i], '\n', out);
232 out->push_back("");
233 }
234 } else {
235 std::cerr << "Unknown comment type " << type << std::endl;
236 abort();
237 }
238 }
239
240 // Add prefix and newline to each comment line and concatenate them together.
241 // 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)242 inline std::string GenerateCommentsWithPrefix(
243 const std::vector<std::string>& in, const std::string& prefix) {
244 std::ostringstream oss;
245 for (auto it = in.begin(); it != in.end(); it++) {
246 const std::string& elem = *it;
247 if (elem.empty()) {
248 oss << prefix << "\n";
249 } else if (elem[0] == ' ') {
250 oss << prefix << EscapeVariableDelimiters(elem) << "\n";
251 } else {
252 oss << prefix << " " << EscapeVariableDelimiters(elem) << "\n";
253 }
254 }
255 return oss.str();
256 }
257
258 template <typename DescriptorType>
GetPrefixedComments(const DescriptorType * desc,bool leading,const std::string & prefix)259 inline std::string GetPrefixedComments(const DescriptorType* desc, bool leading,
260 const std::string& prefix) {
261 std::vector<std::string> out;
262 if (leading) {
263 grpc_generator::GetComment(
264 desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &out);
265 std::vector<std::string> leading;
266 grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
267 &leading);
268 out.insert(out.end(), leading.begin(), leading.end());
269 } else {
270 grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
271 &out);
272 }
273 return GenerateCommentsWithPrefix(out, prefix);
274 }
275
276 } // namespace grpc_generator
277
278 #endif // GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
279