in compiler/cpp/src/thrift/generate/t_go_generator.cc [2293:2853]
void t_go_generator::generate_service_remote(t_service* tservice) {
vector<t_function*> functions;
std::unordered_map<std::string, std::string> func_to_service;
// collect all functions including inherited functions
t_service* parent = tservice;
while (parent != nullptr) {
vector<t_function*> p_functions = parent->get_functions();
functions.insert(functions.end(), p_functions.begin(), p_functions.end());
// We need to maintain a map of functions names to the name of their parent.
// This is because functions may come from a parent service, and if we need
// to create the arguments struct (e.g. `NewParentServiceNameFuncNameArgs()`)
// we need to make sure to specify the correct service name.
for (vector<t_function*>::iterator f_iter = p_functions.begin(); f_iter != p_functions.end(); ++f_iter) {
auto it = func_to_service.find((*f_iter)->get_name());
if (it == func_to_service.end()) {
func_to_service.emplace((*f_iter)->get_name(), parent->get_name());
}
}
parent = parent->get_extends();
}
// This file is not useful if there are no functions; don't generate it
if (functions.size() == 0) {
return;
}
string f_remote_dir = package_dir_ + "/" + underscore(service_name_) + "-remote";
MKDIR(f_remote_dir.c_str());
vector<t_function*>::iterator f_iter;
string f_remote_name = f_remote_dir + "/"
+ underscore(service_name_) + "-remote.go";
ofstream_with_content_based_conditional_update f_remote;
f_remote.open(f_remote_name.c_str());
string unused_protection;
std::vector<string> system_packages;
system_packages.push_back("context");
system_packages.push_back("flag");
system_packages.push_back("fmt");
system_packages.push_back("math");
system_packages.push_back("net");
system_packages.push_back("net/url");
system_packages.push_back("os");
system_packages.push_back("strconv");
system_packages.push_back("strings");
// For the thrift import, always do rename import to make sure it's called thrift.
system_packages.push_back("thrift \"" + gen_thrift_import_ + "\"");
f_remote << go_autogen_comment();
f_remote << indent() << "package main" << '\n' << '\n';
f_remote << indent() << "import (" << '\n';
f_remote << render_system_packages(system_packages);
f_remote << indent() << render_included_programs(unused_protection);
f_remote << render_program_import(program_, unused_protection);
f_remote << indent() << ")" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << unused_protection; // filled in render_included_programs()
f_remote << indent() << '\n';
f_remote << indent() << "func Usage() {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Usage of \", os.Args[0], \" "
"[-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:\")"
<< '\n';
f_remote << indent() << "flag.PrintDefaults()" << '\n';
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"\\nFunctions:\")" << '\n';
string package_name_aliased = package_identifiers_[get_real_go_module(program_)];
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
f_remote << indent() << "fmt.Fprintln(os.Stderr, \" " << (*f_iter)->get_returntype()->get_name() << " "
<< (*f_iter)->get_name() << "(";
t_struct* arg_struct = (*f_iter)->get_arglist();
const std::vector<t_field*>& args = arg_struct->get_members();
std::vector<t_field*>::size_type num_args = args.size();
bool first = true;
for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
if (first) {
first = false;
} else {
f_remote << ", ";
}
f_remote << args[i]->get_type()->get_name() << " " << args[i]->get_name();
}
f_remote << ")\")" << '\n';
}
f_remote << indent() << "fmt.Fprintln(os.Stderr)" << '\n';
f_remote << indent() << "os.Exit(0)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "type httpHeaders map[string]string" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "func (h httpHeaders) String() string {" << '\n';
indent_up();
f_remote << indent() << "var m map[string]string = h" << '\n';
f_remote << indent() << "return fmt.Sprintf(\"%s\", m)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "func (h httpHeaders) Set(value string) error {" << '\n';
indent_up();
f_remote << indent() << "parts := strings.Split(value, \": \")" << '\n';
f_remote << indent() << "if len(parts) != 2 {" << '\n';
indent_up();
f_remote << indent() << "return fmt.Errorf(\"header should be of format 'Key: Value'\")" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "h[parts[0]] = parts[1]" << '\n';
f_remote << indent() << "return nil" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "func main() {" << '\n';
indent_up();
f_remote << indent() << "flag.Usage = Usage" << '\n';
f_remote << indent() << "var host string" << '\n';
f_remote << indent() << "var port int" << '\n';
f_remote << indent() << "var protocol string" << '\n';
f_remote << indent() << "var urlString string" << '\n';
f_remote << indent() << "var framed bool" << '\n';
f_remote << indent() << "var useHttp bool" << '\n';
f_remote << indent() << "headers := make(httpHeaders)" << '\n';
f_remote << indent() << "var parsedUrl *url.URL" << '\n';
f_remote << indent() << "var trans thrift.TTransport" << '\n';
f_remote << indent() << "_ = strconv.Atoi" << '\n';
f_remote << indent() << "_ = math.Abs" << '\n';
f_remote << indent() << "flag.Usage = Usage" << '\n';
f_remote << indent() << "flag.StringVar(&host, \"h\", \"localhost\", \"Specify host and port\")"
<< '\n';
f_remote << indent() << "flag.IntVar(&port, \"p\", 9090, \"Specify port\")" << '\n';
f_remote << indent() << "flag.StringVar(&protocol, \"P\", \"binary\", \""
"Specify the protocol (binary, compact, simplejson, json)\")" << '\n';
f_remote << indent() << "flag.StringVar(&urlString, \"u\", \"\", \"Specify the url\")" << '\n';
f_remote << indent() << "flag.BoolVar(&framed, \"framed\", false, \"Use framed transport\")"
<< '\n';
f_remote << indent() << "flag.BoolVar(&useHttp, \"http\", false, \"Use http\")" << '\n';
f_remote << indent() << "flag.Var(headers, \"H\", \"Headers to set on the http(s) request (e.g. -H \\\"Key: Value\\\")\")" << '\n';
f_remote << indent() << "flag.Parse()" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "if len(urlString) > 0 {" << '\n';
indent_up();
f_remote << indent() << "var err error" << '\n';
f_remote << indent() << "parsedUrl, err = url.Parse(urlString)" << '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << '\n';
f_remote << indent() << "flag.Usage()" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "host = parsedUrl.Host" << '\n';
f_remote << indent() << "useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == \"http\" || parsedUrl.Scheme == \"https\""
<< '\n';
indent_down();
f_remote << indent() << "} else if useHttp {" << '\n';
indent_up();
f_remote << indent() << "_, err := url.Parse(fmt.Sprint(\"http://\", host, \":\", port))"
<< '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << '\n';
f_remote << indent() << "flag.Usage()" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "cmd := flag.Arg(0)" << '\n';
f_remote << indent() << "var err error" << '\n';
f_remote << indent() << "var cfg *thrift.TConfiguration = nil" << '\n';
f_remote << indent() << "if useHttp {" << '\n';
indent_up();
f_remote << indent() << "trans, err = thrift.NewTHttpClient(parsedUrl.String())" << '\n';
f_remote << indent() << "if len(headers) > 0 {" << '\n';
indent_up();
f_remote << indent() << "httptrans := trans.(*thrift.THttpClient)" << '\n';
f_remote << indent() << "for key, value := range headers {" << '\n';
indent_up();
f_remote << indent() << "httptrans.SetHeader(key, value)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "} else {" << '\n';
indent_up();
f_remote << indent() << "portStr := fmt.Sprint(port)" << '\n';
f_remote << indent() << "if strings.Contains(host, \":\") {" << '\n';
indent_up();
f_remote << indent() << "host, portStr, err = net.SplitHostPort(host)" << '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"error with host:\", err)"
<< '\n';
f_remote << indent() << "os.Exit(1)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "trans = thrift.NewTSocketConf(net.JoinHostPort(host, portStr), cfg)" << '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"error resolving address:\", err)" << '\n';
f_remote << indent() << "os.Exit(1)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "if framed {" << '\n';
indent_up();
f_remote << indent() << "trans = thrift.NewTFramedTransportConf(trans, cfg)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Error creating transport\", err)" << '\n';
f_remote << indent() << "os.Exit(1)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "defer trans.Close()" << '\n';
f_remote << indent() << "var protocolFactory thrift.TProtocolFactory" << '\n';
f_remote << indent() << "switch protocol {" << '\n';
f_remote << indent() << "case \"compact\":" << '\n';
indent_up();
f_remote << indent() << "protocolFactory = thrift.NewTCompactProtocolFactoryConf(cfg)" << '\n';
indent_down();
f_remote << indent() << "case \"simplejson\":" << '\n';
indent_up();
f_remote << indent() << "protocolFactory = thrift.NewTSimpleJSONProtocolFactoryConf(cfg)" << '\n';
indent_down();
f_remote << indent() << "case \"json\":" << '\n';
indent_up();
f_remote << indent() << "protocolFactory = thrift.NewTJSONProtocolFactory()" << '\n';
indent_down();
f_remote << indent() << "case \"binary\", \"\":" << '\n';
indent_up();
f_remote << indent() << "protocolFactory = thrift.NewTBinaryProtocolFactoryConf(cfg)" << '\n';
indent_down();
f_remote << indent() << "default:" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Invalid protocol specified: \", protocol)"
<< '\n';
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "os.Exit(1)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "iprot := protocolFactory.GetProtocol(trans)" << '\n';
f_remote << indent() << "oprot := protocolFactory.GetProtocol(trans)" << '\n';
f_remote << indent() << "client := " << package_name_aliased << ".New" << publicize(service_name_)
<< "Client(thrift.NewTStandardClient(iprot, oprot))" << '\n';
f_remote << indent() << "if err := trans.Open(); err != nil {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Error opening socket to \", "
"host, \":\", port, \" \", err)" << '\n';
f_remote << indent() << "os.Exit(1)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << '\n';
f_remote << indent() << "switch cmd {" << '\n';
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
t_struct* arg_struct = (*f_iter)->get_arglist();
const std::vector<t_field*>& args = arg_struct->get_members();
std::vector<t_field*>::size_type num_args = args.size();
string funcName((*f_iter)->get_name());
string pubName(publicize(funcName));
string argumentsName(publicize(funcName + "_args", true, func_to_service[funcName]));
f_remote << indent() << "case \"" << escape_string(funcName) << "\":" << '\n';
indent_up();
f_remote << indent() << "if flag.NArg() - 1 != " << num_args << " {" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"" << escape_string(pubName) << " requires "
<< num_args << " args\")" << '\n';
f_remote << indent() << "flag.Usage()" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
std::vector<t_field*>::size_type flagArg = i + 1;
t_type* the_type(args[i]->get_type());
t_type* the_type2(get_true_type(the_type));
if (the_type2->is_enum()) {
f_remote << indent() << "tmp" << i << ", err := (strconv.Atoi(flag.Arg(" << flagArg << ")))"
<< '\n';
f_remote << indent() << "if err != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "argvalue" << i << " := " << package_name_aliased << "."
<< publicize(the_type->get_name()) << "(tmp" << i << ")" << '\n';
} else if (the_type2->is_base_type()) {
t_base_type::t_base e = ((t_base_type*)the_type2)->get_base();
string err(tmp("err"));
switch (e) {
case t_base_type::TYPE_VOID:
break;
case t_base_type::TYPE_STRING:
if (the_type2->is_binary()) {
f_remote << indent() << "argvalue" << i << " := []byte(flag.Arg(" << flagArg << "))"
<< '\n';
} else {
f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ")" << '\n';
}
break;
case t_base_type::TYPE_BOOL:
f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ") == \"true\""
<< '\n';
break;
case t_base_type::TYPE_I8:
f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
<< flagArg << ")))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "argvalue" << i << " := int8(tmp" << i << ")" << '\n';
break;
case t_base_type::TYPE_I16:
f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
<< flagArg << ")))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "argvalue" << i << " := int16(tmp" << i << ")" << '\n';
break;
case t_base_type::TYPE_I32:
f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
<< flagArg << ")))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "argvalue" << i << " := int32(tmp" << i << ")" << '\n';
break;
case t_base_type::TYPE_I64:
f_remote << indent() << "argvalue" << i << ", " << err
<< " := (strconv.ParseInt(flag.Arg(" << flagArg << "), 10, 64))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
break;
case t_base_type::TYPE_DOUBLE:
f_remote << indent() << "argvalue" << i << ", " << err
<< " := (strconv.ParseFloat(flag.Arg(" << flagArg << "), 64))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
break;
case t_base_type::TYPE_UUID:
f_remote << indent() << "argvalue" << i << ", " << err
<< " := (thrift.ParseTuuid(flag.Arg(" << flagArg << ")))" << '\n';
f_remote << indent() << "if " << err << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
break;
default:
throw("Invalid base type in generate_service_remote");
}
// f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg <<
// ")))";
} else if (the_type2->is_struct()) {
string arg(tmp("arg"));
string mbTrans(tmp("mbTrans"));
string err1(tmp("err"));
string factory(tmp("factory"));
string jsProt(tmp("jsProt"));
string err2(tmp("err"));
std::string tstruct_name(publicize(the_type->get_name()));
std::string tstruct_module( module_name(the_type));
if(tstruct_module.empty()) {
tstruct_module = package_name_aliased;
}
f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << '\n';
f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))"
<< '\n';
f_remote << indent() << "defer " << mbTrans << ".Close()" << '\n';
f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")"
<< '\n';
f_remote << indent() << "if " << err1 << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << factory << " := thrift.NewTJSONProtocolFactory()" << '\n';
f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")"
<< '\n';
f_remote << indent() << "argvalue" << i << " := " << tstruct_module << ".New" << tstruct_name
<< "()" << '\n';
f_remote << indent() << err2 << " := argvalue" << i << "." << read_method_name_ << "(context.Background(), " << jsProt << ")" << '\n';
f_remote << indent() << "if " << err2 << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
} else if (the_type2->is_container() || the_type2->is_xception()) {
string arg(tmp("arg"));
string mbTrans(tmp("mbTrans"));
string err1(tmp("err"));
string factory(tmp("factory"));
string jsProt(tmp("jsProt"));
string err2(tmp("err"));
std::string argName(publicize(args[i]->get_name()));
f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << '\n';
f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))"
<< '\n';
f_remote << indent() << "defer " << mbTrans << ".Close()" << '\n';
f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")"
<< '\n';
f_remote << indent() << "if " << err1 << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << factory << " := thrift.NewTJSONProtocolFactory()" << '\n';
f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")"
<< '\n';
f_remote << indent() << "containerStruct" << i << " := " << package_name_aliased << ".New"
<< argumentsName << "()" << '\n';
f_remote << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(context.Background(), "
<< jsProt << ")" << '\n';
f_remote << indent() << "if " << err2 << " != nil {" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
f_remote << indent() << "return" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
f_remote << indent() << "argvalue" << i << " := containerStruct" << i << "." << argName
<< '\n';
} else {
throw("Invalid argument type in generate_service_remote");
}
if (the_type->is_typedef()) {
std::string typedef_module(module_name(the_type));
if(typedef_module.empty()) {
typedef_module = package_name_aliased;
}
f_remote << indent() << "value" << i << " := " << typedef_module << "."
<< publicize(the_type->get_name()) << "(argvalue" << i << ")" << '\n';
} else {
f_remote << indent() << "value" << i << " := argvalue" << i << '\n';
}
}
f_remote << indent() << "fmt.Print(client." << pubName << "(";
bool argFirst = true;
f_remote << "context.Background()";
for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
if (argFirst) {
argFirst = false;
f_remote << ", ";
} else {
f_remote << ", ";
}
if (args[i]->get_type()->is_enum()) {
f_remote << "value" << i;
} else if (args[i]->get_type()->is_base_type()) {
t_base_type::t_base e = ((t_base_type*)(args[i]->get_type()))->get_base();
switch (e) {
case t_base_type::TYPE_VOID:
break;
case t_base_type::TYPE_STRING:
case t_base_type::TYPE_BOOL:
case t_base_type::TYPE_I8:
case t_base_type::TYPE_I16:
case t_base_type::TYPE_I32:
case t_base_type::TYPE_I64:
case t_base_type::TYPE_DOUBLE:
case t_base_type::TYPE_UUID:
f_remote << "value" << i;
break;
default:
throw("Invalid base type in generate_service_remote");
}
// f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg <<
// ")))";
} else {
f_remote << "value" << i;
}
}
f_remote << "))" << '\n';
f_remote << indent() << "fmt.Print(\"\\n\")" << '\n';
f_remote << indent() << "break" << '\n';
indent_down();
}
f_remote << indent() << "case \"\":" << '\n';
indent_up();
f_remote << indent() << "Usage()" << '\n';
indent_down();
f_remote << indent() << "default:" << '\n';
indent_up();
f_remote << indent() << "fmt.Fprintln(os.Stderr, \"Invalid function \", cmd)" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
indent_down();
f_remote << indent() << "}" << '\n';
// Close service file
f_remote.close();
format_go_output(f_remote_name);
#ifndef _MSC_VER
// Make file executable, love that bitwise OR action
chmod(f_remote_name.c_str(),
S_IRUSR | S_IWUSR | S_IXUSR
#ifndef _WIN32
| S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
#endif
);
#endif
}