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 CommentType { 179 COMMENTTYPE_LEADING, 180 COMMENTTYPE_TRAILING, 181 COMMENTTYPE_LEADING_DETACHED 182 }; 183 184 // Get all the raw comments and append each line without newline to out. 185 template <typename DescriptorType> 186 inline void GetComment(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& comments = 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 GetComment(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 GenerateCommentsWithPrefix( 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 GetPrefixedComments(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