• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <map>
20 #include <set>
21 #include <sstream>
22 
23 #include "src/compiler/config.h"
24 #include "src/compiler/objective_c_generator.h"
25 #include "src/compiler/objective_c_generator_helpers.h"
26 
27 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
28 
29 using ::google::protobuf::compiler::objectivec::ClassName;
30 using ::grpc::protobuf::FileDescriptor;
31 using ::grpc::protobuf::FileDescriptor;
32 using ::grpc::protobuf::MethodDescriptor;
33 using ::grpc::protobuf::ServiceDescriptor;
34 using ::grpc::protobuf::io::Printer;
35 using ::std::map;
36 using ::std::set;
37 
38 namespace grpc_objective_c_generator {
39 namespace {
40 
PrintProtoRpcDeclarationAsPragma(Printer * printer,const MethodDescriptor * method,map<::grpc::string,::grpc::string> vars)41 void PrintProtoRpcDeclarationAsPragma(
42     Printer* printer, const MethodDescriptor* method,
43     map< ::grpc::string, ::grpc::string> vars) {
44   vars["client_stream"] = method->client_streaming() ? "stream " : "";
45   vars["server_stream"] = method->server_streaming() ? "stream " : "";
46 
47   printer->Print(vars,
48                  "#pragma mark $method_name$($client_stream$$request_type$)"
49                  " returns ($server_stream$$response_type$)\n\n");
50 }
51 
52 template <typename DescriptorType>
PrintAllComments(const DescriptorType * desc,Printer * printer)53 static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
54   std::vector<grpc::string> comments;
55   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
56                              &comments);
57   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
58                              &comments);
59   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
60                              &comments);
61   if (comments.empty()) {
62     return;
63   }
64   printer->Print("/**\n");
65   for (auto it = comments.begin(); it != comments.end(); ++it) {
66     printer->Print(" * ");
67     size_t start_pos = it->find_first_not_of(' ');
68     if (start_pos != grpc::string::npos) {
69       printer->PrintRaw(it->c_str() + start_pos);
70     }
71     printer->Print("\n");
72   }
73   printer->Print(" */\n");
74 }
75 
PrintMethodSignature(Printer * printer,const MethodDescriptor * method,const map<::grpc::string,::grpc::string> & vars)76 void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
77                           const map< ::grpc::string, ::grpc::string>& vars) {
78   // Print comment
79   PrintAllComments(method, printer);
80 
81   printer->Print(vars, "- ($return_type$)$method_name$With");
82   if (method->client_streaming()) {
83     printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
84   } else {
85     printer->Print(vars, "Request:($request_class$ *)request");
86   }
87 
88   // TODO(jcanizales): Put this on a new line and align colons.
89   if (method->server_streaming()) {
90     printer->Print(vars,
91                    " eventHandler:(void(^)(BOOL done, "
92                    "$response_class$ *_Nullable response, NSError *_Nullable "
93                    "error))eventHandler");
94   } else {
95     printer->Print(vars,
96                    " handler:(void(^)($response_class$ *_Nullable response, "
97                    "NSError *_Nullable error))handler");
98   }
99 }
100 
PrintSimpleSignature(Printer * printer,const MethodDescriptor * method,map<::grpc::string,::grpc::string> vars)101 void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
102                           map< ::grpc::string, ::grpc::string> vars) {
103   vars["method_name"] =
104       grpc_generator::LowercaseFirstLetter(vars["method_name"]);
105   vars["return_type"] = "void";
106   PrintMethodSignature(printer, method, vars);
107 }
108 
PrintAdvancedSignature(Printer * printer,const MethodDescriptor * method,map<::grpc::string,::grpc::string> vars)109 void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
110                             map< ::grpc::string, ::grpc::string> vars) {
111   vars["method_name"] = "RPCTo" + vars["method_name"];
112   vars["return_type"] = "GRPCProtoCall *";
113   PrintMethodSignature(printer, method, vars);
114 }
115 
GetMethodVars(const MethodDescriptor * method)116 inline map< ::grpc::string, ::grpc::string> GetMethodVars(
117     const MethodDescriptor* method) {
118   map< ::grpc::string, ::grpc::string> res;
119   res["method_name"] = method->name();
120   res["request_type"] = method->input_type()->name();
121   res["response_type"] = method->output_type()->name();
122   res["request_class"] = ClassName(method->input_type());
123   res["response_class"] = ClassName(method->output_type());
124   return res;
125 }
126 
PrintMethodDeclarations(Printer * printer,const MethodDescriptor * method)127 void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
128   map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
129 
130   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
131 
132   PrintSimpleSignature(printer, method, vars);
133   printer->Print(";\n\n");
134   PrintAdvancedSignature(printer, method, vars);
135   printer->Print(";\n\n\n");
136 }
137 
PrintSimpleImplementation(Printer * printer,const MethodDescriptor * method,map<::grpc::string,::grpc::string> vars)138 void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
139                                map< ::grpc::string, ::grpc::string> vars) {
140   printer->Print("{\n");
141   printer->Print(vars, "  [[self RPCTo$method_name$With");
142   if (method->client_streaming()) {
143     printer->Print("RequestsWriter:requestWriter");
144   } else {
145     printer->Print("Request:request");
146   }
147   if (method->server_streaming()) {
148     printer->Print(" eventHandler:eventHandler] start];\n");
149   } else {
150     printer->Print(" handler:handler] start];\n");
151   }
152   printer->Print("}\n");
153 }
154 
PrintAdvancedImplementation(Printer * printer,const MethodDescriptor * method,map<::grpc::string,::grpc::string> vars)155 void PrintAdvancedImplementation(Printer* printer,
156                                  const MethodDescriptor* method,
157                                  map< ::grpc::string, ::grpc::string> vars) {
158   printer->Print("{\n");
159   printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
160 
161   printer->Print("            requestsWriter:");
162   if (method->client_streaming()) {
163     printer->Print("requestWriter\n");
164   } else {
165     printer->Print("[GRXWriter writerWithValue:request]\n");
166   }
167 
168   printer->Print(vars, "             responseClass:[$response_class$ class]\n");
169 
170   printer->Print("        responsesWriteable:[GRXWriteable ");
171   if (method->server_streaming()) {
172     printer->Print("writeableWithEventHandler:eventHandler]];\n");
173   } else {
174     printer->Print("writeableWithSingleHandler:handler]];\n");
175   }
176 
177   printer->Print("}\n");
178 }
179 
PrintMethodImplementations(Printer * printer,const MethodDescriptor * method)180 void PrintMethodImplementations(Printer* printer,
181                                 const MethodDescriptor* method) {
182   map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
183 
184   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
185 
186   // TODO(jcanizales): Print documentation from the method.
187   PrintSimpleSignature(printer, method, vars);
188   PrintSimpleImplementation(printer, method, vars);
189 
190   printer->Print("// Returns a not-yet-started RPC object.\n");
191   PrintAdvancedSignature(printer, method, vars);
192   PrintAdvancedImplementation(printer, method, vars);
193 }
194 
195 }  // namespace
196 
GetAllMessageClasses(const FileDescriptor * file)197 ::grpc::string GetAllMessageClasses(const FileDescriptor* file) {
198   ::grpc::string output;
199   set< ::grpc::string> classes;
200   for (int i = 0; i < file->service_count(); i++) {
201     const auto service = file->service(i);
202     for (int i = 0; i < service->method_count(); i++) {
203       const auto method = service->method(i);
204       classes.insert(ClassName(method->input_type()));
205       classes.insert(ClassName(method->output_type()));
206     }
207   }
208   for (auto one_class : classes) {
209     output += "@class " + one_class + ";\n";
210   }
211 
212   return output;
213 }
214 
GetProtocol(const ServiceDescriptor * service)215 ::grpc::string GetProtocol(const ServiceDescriptor* service) {
216   ::grpc::string output;
217 
218   // Scope the output stream so it closes and finalizes output to the string.
219   grpc::protobuf::io::StringOutputStream output_stream(&output);
220   Printer printer(&output_stream, '$');
221 
222   map< ::grpc::string, ::grpc::string> vars = {
223       {"service_class", ServiceClassName(service)}};
224 
225   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
226   for (int i = 0; i < service->method_count(); i++) {
227     PrintMethodDeclarations(&printer, service->method(i));
228   }
229   printer.Print("@end\n\n");
230 
231   return output;
232 }
233 
GetInterface(const ServiceDescriptor * service)234 ::grpc::string GetInterface(const ServiceDescriptor* service) {
235   ::grpc::string output;
236 
237   // Scope the output stream so it closes and finalizes output to the string.
238   grpc::protobuf::io::StringOutputStream output_stream(&output);
239   Printer printer(&output_stream, '$');
240 
241   map< ::grpc::string, ::grpc::string> vars = {
242       {"service_class", ServiceClassName(service)}};
243 
244   printer.Print(vars,
245                 "/**\n"
246                 " * Basic service implementation, over gRPC, that only does\n"
247                 " * marshalling and parsing.\n"
248                 " */\n");
249   printer.Print(vars,
250                 "@interface $service_class$ :"
251                 " GRPCProtoService<$service_class$>\n");
252   printer.Print(
253       "- (instancetype)initWithHost:(NSString *)host"
254       " NS_DESIGNATED_INITIALIZER;\n");
255   printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
256   printer.Print("@end\n");
257 
258   return output;
259 }
260 
GetSource(const ServiceDescriptor * service)261 ::grpc::string GetSource(const ServiceDescriptor* service) {
262   ::grpc::string output;
263   {
264     // Scope the output stream so it closes and finalizes output to the string.
265     grpc::protobuf::io::StringOutputStream output_stream(&output);
266     Printer printer(&output_stream, '$');
267 
268     map< ::grpc::string, ::grpc::string> vars = {
269         {"service_name", service->name()},
270         {"service_class", ServiceClassName(service)},
271         {"package", service->file()->package()}};
272 
273     printer.Print(vars,
274                   "@implementation $service_class$\n\n"
275                   "// Designated initializer\n"
276                   "- (instancetype)initWithHost:(NSString *)host {\n"
277                   "  self = [super initWithHost:host\n"
278                   "                 packageName:@\"$package$\"\n"
279                   "                 serviceName:@\"$service_name$\"];\n"
280                   "  return self;\n"
281                   "}\n\n");
282 
283     printer.Print(
284         "// Override superclass initializer to disallow different"
285         " package and service names.\n"
286         "- (instancetype)initWithHost:(NSString *)host\n"
287         "                 packageName:(NSString *)packageName\n"
288         "                 serviceName:(NSString *)serviceName {\n"
289         "  return [self initWithHost:host];\n"
290         "}\n\n");
291 
292     printer.Print(
293         "#pragma mark - Class Methods\n\n"
294         "+ (instancetype)serviceWithHost:(NSString *)host {\n"
295         "  return [[self alloc] initWithHost:host];\n"
296         "}\n\n");
297 
298     printer.Print("#pragma mark - Method Implementations\n\n");
299 
300     for (int i = 0; i < service->method_count(); i++) {
301       PrintMethodImplementations(&printer, service->method(i));
302     }
303 
304     printer.Print("@end\n");
305   }
306   return output;
307 }
308 
309 }  // namespace grpc_objective_c_generator
310