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 <cctype> |
20 | #include <map> |
21 | #include <vector> |
22 | |
23 | #include "src/compiler/config.h" |
24 | #include "src/compiler/ruby_generator.h" |
25 | #include "src/compiler/ruby_generator_helpers-inl.h" |
26 | #include "src/compiler/ruby_generator_map-inl.h" |
27 | #include "src/compiler/ruby_generator_string-inl.h" |
28 | |
29 | using grpc::protobuf::FileDescriptor; |
30 | using grpc::protobuf::MethodDescriptor; |
31 | using grpc::protobuf::ServiceDescriptor; |
32 | using grpc::protobuf::io::Printer; |
33 | using grpc::protobuf::io::StringOutputStream; |
34 | using std::map; |
35 | using std::vector; |
36 | |
37 | namespace grpc_ruby_generator { |
38 | namespace { |
39 | |
40 | // Prints out the method using the ruby gRPC DSL. |
41 | void PrintMethod(const MethodDescriptor* method, const grpc::string& package, |
42 | Printer* out) { |
43 | grpc::string input_type = RubyTypeOf(method->input_type(), package); |
44 | if (method->client_streaming()) { |
45 | input_type = "stream(" + input_type + ")" ; |
46 | } |
47 | grpc::string output_type = RubyTypeOf(method->output_type(), package); |
48 | if (method->server_streaming()) { |
49 | output_type = "stream(" + output_type + ")" ; |
50 | } |
51 | std::map<grpc::string, grpc::string> method_vars = ListToDict({ |
52 | "mth.name" , |
53 | method->name(), |
54 | "input.type" , |
55 | input_type, |
56 | "output.type" , |
57 | output_type, |
58 | }); |
59 | out->Print(GetRubyComments(method, true).c_str()); |
60 | out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n" ); |
61 | out->Print(GetRubyComments(method, false).c_str()); |
62 | } |
63 | |
64 | // Prints out the service using the ruby gRPC DSL. |
65 | void PrintService(const ServiceDescriptor* service, const grpc::string& package, |
66 | Printer* out) { |
67 | if (service->method_count() == 0) { |
68 | return; |
69 | } |
70 | |
71 | // Begin the service module |
72 | std::map<grpc::string, grpc::string> module_vars = ListToDict({ |
73 | "module.name" , |
74 | Modularize(service->name()), |
75 | }); |
76 | out->Print(module_vars, "module $module.name$\n" ); |
77 | out->Indent(); |
78 | |
79 | out->Print(GetRubyComments(service, true).c_str()); |
80 | out->Print("class Service\n" ); |
81 | |
82 | // Write the indented class body. |
83 | out->Indent(); |
84 | out->Print("\n" ); |
85 | out->Print("include GRPC::GenericService\n" ); |
86 | out->Print("\n" ); |
87 | out->Print("self.marshal_class_method = :encode\n" ); |
88 | out->Print("self.unmarshal_class_method = :decode\n" ); |
89 | std::map<grpc::string, grpc::string> pkg_vars = |
90 | ListToDict({"service_full_name" , service->full_name()}); |
91 | out->Print(pkg_vars, "self.service_name = '$service_full_name$'\n" ); |
92 | out->Print("\n" ); |
93 | for (int i = 0; i < service->method_count(); ++i) { |
94 | PrintMethod(service->method(i), package, out); |
95 | } |
96 | out->Outdent(); |
97 | |
98 | out->Print("end\n" ); |
99 | out->Print("\n" ); |
100 | out->Print("Stub = Service.rpc_stub_class\n" ); |
101 | |
102 | // End the service module |
103 | out->Outdent(); |
104 | out->Print("end\n" ); |
105 | out->Print(GetRubyComments(service, false).c_str()); |
106 | } |
107 | |
108 | } // namespace |
109 | |
110 | // The following functions are copied directly from the source for the protoc |
111 | // ruby generator |
112 | // to ensure compatibility (with the exception of int and string type changes). |
113 | // See |
114 | // https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/ruby/ruby_generator.cc#L250 |
115 | // TODO: keep up to date with protoc code generation, though this behavior isn't |
116 | // expected to change |
117 | bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } |
118 | |
119 | char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } |
120 | |
121 | // Package names in protobuf are snake_case by convention, but Ruby module |
122 | // names must be PascalCased. |
123 | // |
124 | // foo_bar_baz -> FooBarBaz |
125 | grpc::string PackageToModule(const grpc::string& name) { |
126 | bool next_upper = true; |
127 | grpc::string result; |
128 | result.reserve(name.size()); |
129 | |
130 | for (grpc::string::size_type i = 0; i < name.size(); i++) { |
131 | if (name[i] == '_') { |
132 | next_upper = true; |
133 | } else { |
134 | if (next_upper) { |
135 | result.push_back(ToUpper(name[i])); |
136 | } else { |
137 | result.push_back(name[i]); |
138 | } |
139 | next_upper = false; |
140 | } |
141 | } |
142 | |
143 | return result; |
144 | } |
145 | // end copying of protoc generator for ruby code |
146 | |
147 | grpc::string GetServices(const FileDescriptor* file) { |
148 | grpc::string output; |
149 | { |
150 | // Scope the output stream so it closes and finalizes output to the string. |
151 | |
152 | StringOutputStream output_stream(&output); |
153 | Printer out(&output_stream, '$'); |
154 | |
155 | // Don't write out any output if there no services, to avoid empty service |
156 | // files being generated for proto files that don't declare any. |
157 | if (file->service_count() == 0) { |
158 | return output; |
159 | } |
160 | |
161 | std::string package_name = RubyPackage(file); |
162 | |
163 | // Write out a file header. |
164 | std::map<grpc::string, grpc::string> = ListToDict({ |
165 | "file.name" , |
166 | file->name(), |
167 | "file.package" , |
168 | package_name, |
169 | }); |
170 | out.Print("# Generated by the protocol buffer compiler. DO NOT EDIT!\n" ); |
171 | out.Print(header_comment_vars, |
172 | "# Source: $file.name$ for package '$file.package$'\n" ); |
173 | |
174 | grpc::string = GetRubyComments(file, true); |
175 | if (!leading_comments.empty()) { |
176 | out.Print("# Original file comments:\n" ); |
177 | out.PrintRaw(leading_comments.c_str()); |
178 | } |
179 | |
180 | out.Print("\n" ); |
181 | out.Print("require 'grpc'\n" ); |
182 | // Write out require statemment to import the separately generated file |
183 | // that defines the messages used by the service. This is generated by the |
184 | // main ruby plugin. |
185 | std::map<grpc::string, grpc::string> dep_vars = ListToDict({ |
186 | "dep.name" , |
187 | MessagesRequireName(file), |
188 | }); |
189 | out.Print(dep_vars, "require '$dep.name$'\n" ); |
190 | |
191 | // Write out services within the modules |
192 | out.Print("\n" ); |
193 | std::vector<grpc::string> modules = Split(package_name, '.'); |
194 | for (size_t i = 0; i < modules.size(); ++i) { |
195 | std::map<grpc::string, grpc::string> module_vars = ListToDict({ |
196 | "module.name" , |
197 | PackageToModule(modules[i]), |
198 | }); |
199 | out.Print(module_vars, "module $module.name$\n" ); |
200 | out.Indent(); |
201 | } |
202 | for (int i = 0; i < file->service_count(); ++i) { |
203 | auto service = file->service(i); |
204 | PrintService(service, file->package(), &out); |
205 | } |
206 | for (size_t i = 0; i < modules.size(); ++i) { |
207 | out.Outdent(); |
208 | out.Print("end\n" ); |
209 | } |
210 | |
211 | out.Print(GetRubyComments(file, false).c_str()); |
212 | } |
213 | return output; |
214 | } |
215 | |
216 | } // namespace grpc_ruby_generator |
217 | |