| /* |
| * |
| * Copyright 2015, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <cctype> |
| #include <map> |
| #include <vector> |
| |
| #include "src/compiler/config.h" |
| #include "src/compiler/ruby_generator.h" |
| #include "src/compiler/ruby_generator_helpers-inl.h" |
| #include "src/compiler/ruby_generator_map-inl.h" |
| #include "src/compiler/ruby_generator_string-inl.h" |
| |
| using grpc::protobuf::FileDescriptor; |
| using grpc::protobuf::ServiceDescriptor; |
| using grpc::protobuf::MethodDescriptor; |
| using grpc::protobuf::io::Printer; |
| using grpc::protobuf::io::StringOutputStream; |
| using std::map; |
| using std::vector; |
| |
| namespace grpc_ruby_generator { |
| namespace { |
| |
| // Prints out the method using the ruby gRPC DSL. |
| void PrintMethod(const MethodDescriptor *method, const grpc::string &package, |
| Printer *out) { |
| grpc::string input_type = RubyTypeOf(method->input_type()->full_name(), package); |
| if (method->client_streaming()) { |
| input_type = "stream(" + input_type + ")"; |
| } |
| grpc::string output_type = RubyTypeOf(method->output_type()->full_name(), package); |
| if (method->server_streaming()) { |
| output_type = "stream(" + output_type + ")"; |
| } |
| std::map<grpc::string, grpc::string> method_vars = |
| ListToDict({"mth.name", method->name(), "input.type", input_type, |
| "output.type", output_type, }); |
| out->Print(GetRubyComments(method, true).c_str()); |
| out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n"); |
| out->Print(GetRubyComments(method, false).c_str()); |
| } |
| |
| // Prints out the service using the ruby gRPC DSL. |
| void PrintService(const ServiceDescriptor *service, const grpc::string &package, |
| Printer *out) { |
| if (service->method_count() == 0) { |
| return; |
| } |
| |
| // Begin the service module |
| std::map<grpc::string, grpc::string> module_vars = |
| ListToDict({"module.name", CapitalizeFirst(service->name()), }); |
| out->Print(module_vars, "module $module.name$\n"); |
| out->Indent(); |
| |
| out->Print(GetRubyComments(service, true).c_str()); |
| out->Print("class Service\n"); |
| |
| // Write the indented class body. |
| out->Indent(); |
| out->Print("\n"); |
| out->Print("include GRPC::GenericService\n"); |
| out->Print("\n"); |
| out->Print("self.marshal_class_method = :encode\n"); |
| out->Print("self.unmarshal_class_method = :decode\n"); |
| std::map<grpc::string, grpc::string> pkg_vars = |
| ListToDict({"service_full_name", service->full_name()}); |
| out->Print(pkg_vars, "self.service_name = '$service_full_name$'\n"); |
| out->Print("\n"); |
| for (int i = 0; i < service->method_count(); ++i) { |
| PrintMethod(service->method(i), package, out); |
| } |
| out->Outdent(); |
| |
| out->Print("end\n"); |
| out->Print("\n"); |
| out->Print("Stub = Service.rpc_stub_class\n"); |
| |
| // End the service module |
| out->Outdent(); |
| out->Print("end\n"); |
| out->Print(GetRubyComments(service, false).c_str()); |
| } |
| |
| } // namespace |
| |
| // The following functions are copied directly from the source for the protoc ruby generator |
| // to ensure compatibility (with the exception of int and string type changes). |
| // See https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/ruby/ruby_generator.cc#L250 |
| // TODO: keep up to date with protoc code generation, though this behavior isn't expected to change |
| bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } |
| |
| char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } |
| |
| |
| // Package names in protobuf are snake_case by convention, but Ruby module |
| // names must be PascalCased. |
| // |
| // foo_bar_baz -> FooBarBaz |
| grpc::string PackageToModule(const grpc::string& name) { |
| bool next_upper = true; |
| grpc::string result; |
| result.reserve(name.size()); |
| |
| for (grpc::string::size_type i = 0; i < name.size(); i++) { |
| if (name[i] == '_') { |
| next_upper = true; |
| } else { |
| if (next_upper) { |
| result.push_back(ToUpper(name[i])); |
| } else { |
| result.push_back(name[i]); |
| } |
| next_upper = false; |
| } |
| } |
| |
| return result; |
| } |
| // end copying of protoc generator for ruby code |
| |
| grpc::string GetServices(const FileDescriptor *file) { |
| grpc::string output; |
| { |
| // Scope the output stream so it closes and finalizes output to the string. |
| |
| StringOutputStream output_stream(&output); |
| Printer out(&output_stream, '$'); |
| |
| // Don't write out any output if there no services, to avoid empty service |
| // files being generated for proto files that don't declare any. |
| if (file->service_count() == 0) { |
| return output; |
| } |
| |
| // Write out a file header. |
| std::map<grpc::string, grpc::string> header_comment_vars = ListToDict( |
| {"file.name", file->name(), "file.package", file->package(), }); |
| out.Print("# Generated by the protocol buffer compiler. DO NOT EDIT!\n"); |
| out.Print(header_comment_vars, |
| "# Source: $file.name$ for package '$file.package$'\n"); |
| |
| grpc::string leading_comments = GetRubyComments(file, true); |
| if (!leading_comments.empty()) { |
| out.Print("# Original file comments:\n"); |
| out.Print(leading_comments.c_str()); |
| } |
| |
| out.Print("\n"); |
| out.Print("require 'grpc'\n"); |
| // Write out require statemment to import the separately generated file |
| // that defines the messages used by the service. This is generated by the |
| // main ruby plugin. |
| std::map<grpc::string, grpc::string> dep_vars = |
| ListToDict({"dep.name", MessagesRequireName(file), }); |
| out.Print(dep_vars, "require '$dep.name$'\n"); |
| |
| // Write out services within the modules |
| out.Print("\n"); |
| std::vector<grpc::string> modules = Split(file->package(), '.'); |
| for (size_t i = 0; i < modules.size(); ++i) { |
| std::map<grpc::string, grpc::string> module_vars = |
| ListToDict({"module.name", PackageToModule(modules[i]), }); |
| out.Print(module_vars, "module $module.name$\n"); |
| out.Indent(); |
| } |
| for (int i = 0; i < file->service_count(); ++i) { |
| auto service = file->service(i); |
| PrintService(service, file->package(), &out); |
| } |
| for (size_t i = 0; i < modules.size(); ++i) { |
| out.Outdent(); |
| out.Print("end\n"); |
| } |
| |
| out.Print(GetRubyComments(file, false).c_str()); |
| } |
| return output; |
| } |
| |
| } // namespace grpc_ruby_generator |