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 | |
30 | namespace grpc_generator { |
31 | |
32 | inline bool StripSuffix(grpc::string* filename, const grpc::string& suffix) { |
33 | if (filename->length() >= suffix.length()) { |
34 | size_t suffix_pos = filename->length() - suffix.length(); |
35 | if (filename->compare(suffix_pos, grpc::string::npos, suffix) == 0) { |
36 | filename->resize(filename->size() - suffix.size()); |
37 | return true; |
38 | } |
39 | } |
40 | |
41 | return false; |
42 | } |
43 | |
44 | inline bool StripPrefix(grpc::string* name, const grpc::string& prefix) { |
45 | if (name->length() >= prefix.length()) { |
46 | if (name->substr(0, prefix.size()) == prefix) { |
47 | *name = name->substr(prefix.size()); |
48 | return true; |
49 | } |
50 | } |
51 | return false; |
52 | } |
53 | |
54 | inline grpc::string StripProto(grpc::string filename) { |
55 | if (!StripSuffix(&filename, ".protodevel" )) { |
56 | StripSuffix(&filename, ".proto" ); |
57 | } |
58 | return filename; |
59 | } |
60 | |
61 | inline grpc::string StringReplace(grpc::string str, const grpc::string& from, |
62 | const grpc::string& to, bool replace_all) { |
63 | size_t pos = 0; |
64 | |
65 | do { |
66 | pos = str.find(from, pos); |
67 | if (pos == grpc::string::npos) { |
68 | break; |
69 | } |
70 | str.replace(pos, from.length(), to); |
71 | pos += to.length(); |
72 | } while (replace_all); |
73 | |
74 | return str; |
75 | } |
76 | |
77 | inline grpc::string StringReplace(grpc::string str, const grpc::string& from, |
78 | const grpc::string& to) { |
79 | return StringReplace(str, from, to, true); |
80 | } |
81 | |
82 | inline std::vector<grpc::string> tokenize(const grpc::string& input, |
83 | const grpc::string& delimiters) { |
84 | std::vector<grpc::string> tokens; |
85 | size_t pos, last_pos = 0; |
86 | |
87 | for (;;) { |
88 | bool done = false; |
89 | pos = input.find_first_of(delimiters, last_pos); |
90 | if (pos == grpc::string::npos) { |
91 | done = true; |
92 | pos = input.length(); |
93 | } |
94 | |
95 | tokens.push_back(input.substr(last_pos, pos - last_pos)); |
96 | if (done) return tokens; |
97 | |
98 | last_pos = pos + 1; |
99 | } |
100 | } |
101 | |
102 | inline grpc::string CapitalizeFirstLetter(grpc::string s) { |
103 | if (s.empty()) { |
104 | return s; |
105 | } |
106 | s[0] = ::toupper(s[0]); |
107 | return s; |
108 | } |
109 | |
110 | inline grpc::string LowercaseFirstLetter(grpc::string s) { |
111 | if (s.empty()) { |
112 | return s; |
113 | } |
114 | s[0] = ::tolower(s[0]); |
115 | return s; |
116 | } |
117 | |
118 | inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) { |
119 | std::vector<grpc::string> tokens = tokenize(str, "_" ); |
120 | grpc::string result = "" ; |
121 | for (unsigned int i = 0; i < tokens.size(); i++) { |
122 | result += CapitalizeFirstLetter(tokens[i]); |
123 | } |
124 | return result; |
125 | } |
126 | |
127 | inline grpc::string FileNameInUpperCamel( |
128 | const grpc::protobuf::FileDescriptor* file, bool include_package_path) { |
129 | std::vector<grpc::string> tokens = tokenize(StripProto(file->name()), "/" ); |
130 | grpc::string result = "" ; |
131 | if (include_package_path) { |
132 | for (unsigned int i = 0; i < tokens.size() - 1; i++) { |
133 | result += tokens[i] + "/" ; |
134 | } |
135 | } |
136 | result += LowerUnderscoreToUpperCamel(tokens.back()); |
137 | return result; |
138 | } |
139 | |
140 | inline grpc::string FileNameInUpperCamel( |
141 | const grpc::protobuf::FileDescriptor* file) { |
142 | return FileNameInUpperCamel(file, true); |
143 | } |
144 | |
145 | enum MethodType { |
146 | METHODTYPE_NO_STREAMING, |
147 | METHODTYPE_CLIENT_STREAMING, |
148 | METHODTYPE_SERVER_STREAMING, |
149 | METHODTYPE_BIDI_STREAMING |
150 | }; |
151 | |
152 | inline MethodType GetMethodType( |
153 | const grpc::protobuf::MethodDescriptor* method) { |
154 | if (method->client_streaming()) { |
155 | if (method->server_streaming()) { |
156 | return METHODTYPE_BIDI_STREAMING; |
157 | } else { |
158 | return METHODTYPE_CLIENT_STREAMING; |
159 | } |
160 | } else { |
161 | if (method->server_streaming()) { |
162 | return METHODTYPE_SERVER_STREAMING; |
163 | } else { |
164 | return METHODTYPE_NO_STREAMING; |
165 | } |
166 | } |
167 | } |
168 | |
169 | inline void Split(const grpc::string& s, char /*delim*/, |
170 | std::vector<grpc::string>* append_to) { |
171 | std::istringstream iss(s); |
172 | grpc::string piece; |
173 | while (std::getline(iss, piece)) { |
174 | append_to->push_back(piece); |
175 | } |
176 | } |
177 | |
178 | enum { |
179 | , |
180 | , |
181 | |
182 | }; |
183 | |
184 | // Get all the raw comments and append each line without newline to out. |
185 | template <typename DescriptorType> |
186 | inline void (const DescriptorType* desc, CommentType type, |
187 | std::vector<grpc::string>* out) { |
188 | grpc::protobuf::SourceLocation location; |
189 | if (!desc->GetSourceLocation(&location)) { |
190 | return; |
191 | } |
192 | if (type == COMMENTTYPE_LEADING || type == COMMENTTYPE_TRAILING) { |
193 | const grpc::string& = type == COMMENTTYPE_LEADING |
194 | ? location.leading_comments |
195 | : location.trailing_comments; |
196 | Split(comments, '\n', out); |
197 | } else if (type == COMMENTTYPE_LEADING_DETACHED) { |
198 | for (unsigned int i = 0; i < location.leading_detached_comments.size(); |
199 | i++) { |
200 | Split(location.leading_detached_comments[i], '\n', out); |
201 | out->push_back("" ); |
202 | } |
203 | } else { |
204 | std::cerr << "Unknown comment type " << type << std::endl; |
205 | abort(); |
206 | } |
207 | } |
208 | |
209 | // Each raw comment line without newline is appended to out. |
210 | // For file level leading and detached leading comments, we return comments |
211 | // above syntax line. Return nothing for trailing comments. |
212 | template <> |
213 | inline void (const grpc::protobuf::FileDescriptor* desc, |
214 | CommentType type, std::vector<grpc::string>* out) { |
215 | if (type == COMMENTTYPE_TRAILING) { |
216 | return; |
217 | } |
218 | grpc::protobuf::SourceLocation location; |
219 | std::vector<int> path; |
220 | path.push_back(grpc::protobuf::FileDescriptorProto::kSyntaxFieldNumber); |
221 | if (!desc->GetSourceLocation(path, &location)) { |
222 | return; |
223 | } |
224 | if (type == COMMENTTYPE_LEADING) { |
225 | Split(location.leading_comments, '\n', out); |
226 | } else if (type == COMMENTTYPE_LEADING_DETACHED) { |
227 | for (unsigned int i = 0; i < location.leading_detached_comments.size(); |
228 | i++) { |
229 | Split(location.leading_detached_comments[i], '\n', out); |
230 | out->push_back("" ); |
231 | } |
232 | } else { |
233 | std::cerr << "Unknown comment type " << type << std::endl; |
234 | abort(); |
235 | } |
236 | } |
237 | |
238 | // Add prefix and newline to each comment line and concatenate them together. |
239 | // Make sure there is a space after the prefix unless the line is empty. |
240 | inline grpc::string ( |
241 | const std::vector<grpc::string>& in, const grpc::string& prefix) { |
242 | std::ostringstream oss; |
243 | for (auto it = in.begin(); it != in.end(); it++) { |
244 | const grpc::string& elem = *it; |
245 | if (elem.empty()) { |
246 | oss << prefix << "\n" ; |
247 | } else if (elem[0] == ' ') { |
248 | oss << prefix << elem << "\n" ; |
249 | } else { |
250 | oss << prefix << " " << elem << "\n" ; |
251 | } |
252 | } |
253 | return oss.str(); |
254 | } |
255 | |
256 | template <typename DescriptorType> |
257 | inline grpc::string (const DescriptorType* desc, |
258 | bool leading, |
259 | const grpc::string& prefix) { |
260 | std::vector<grpc::string> out; |
261 | if (leading) { |
262 | grpc_generator::GetComment( |
263 | desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &out); |
264 | std::vector<grpc::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 | |