| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 | /* * * Copyright 2015 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */#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::MethodDescriptor;using grpc::protobuf::ServiceDescriptor;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 changebool 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 -> FooBarBazgrpc::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 codegrpc::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.PrintRaw(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
 |