in compiler/cpp/src/thrift/generate/t_go_generator.cc [1239:1473]
void t_go_generator::generate_go_struct_definition(ostream& out,
t_struct* tstruct,
bool is_exception,
bool is_result,
bool is_args) {
const vector<t_field*>& members = tstruct->get_members();
const vector<t_field*>& sorted_members = tstruct->get_sorted_members();
vector<t_field*>::const_iterator m_iter;
std::string tstruct_name(publicize(tstruct->get_name(), is_args || is_result));
generate_go_docstring(out, tstruct);
generate_deprecation_comment(out, tstruct->annotations_);
out << indent() << "type " << tstruct_name << " struct {" << '\n';
/*
Here we generate the structure specification for the fastbinary codec.
These specifications have the following structure:
thrift_spec -> tuple of item_spec
item_spec -> nil | (tag, type_enum, name, spec_args, default)
tag -> integer
type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ...
name -> string_literal
default -> nil # Handled by __init__
spec_args -> nil # For simple types
| (type_enum, spec_args) # Value type for list/set
| (type_enum, spec_args, type_enum, spec_args)
# Key and value for map
| (class_name, spec_args_ptr) # For struct/exception
class_name -> identifier # Basically a pointer to the class
spec_args_ptr -> expression # just class_name.spec_args
TODO(dreiss): Consider making this work for structs with negative tags.
*/
// TODO(dreiss): Look into generating an empty tuple instead of nil
// for structures with no members.
// TODO(dreiss): Test encoding of structs where some inner structs
// don't have thrift_spec.
indent_up();
int num_setable = 0;
if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) {
int sorted_keys_pos = 0;
for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) {
// Set field to optional if field is union, this is so we can get a
// pointer to the field.
if (tstruct->is_union())
(*m_iter)->set_req(t_field::T_OPTIONAL);
if (sorted_keys_pos != (*m_iter)->get_key()) {
int first_unused = (std::max)(1, sorted_keys_pos++);
while (sorted_keys_pos != (*m_iter)->get_key()) {
++sorted_keys_pos;
}
int last_unused = sorted_keys_pos - 1;
if (first_unused < last_unused) {
indent(out) << "// unused fields # " << first_unused << " to " << last_unused << '\n';
} else if (first_unused == last_unused) {
indent(out) << "// unused field # " << first_unused << '\n';
}
}
t_type* fieldType = (*m_iter)->get_type();
string goType = type_to_go_type_with_opt(fieldType, is_pointer_field(*m_iter));
map<string,string>tags;
tags["db"]=escape_string((*m_iter)->get_name());
// Only add the `omitempty` tag if this field is optional and has no default value.
// Otherwise a proper value like `false` for a bool field will be ommitted from
// the JSON output since Go Marshal won't output `zero values`.
bool has_default = (*m_iter)->get_value();
bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
if (is_optional && !has_default) {
tags["json"]=escape_string((*m_iter)->get_name())+",omitempty";
} else {
tags["json"]=escape_string((*m_iter)->get_name());
}
// Check for user defined tags and them if there are any. User defined tags
// can override the above db and json tags.
std::map<string, std::vector<string>>::iterator it = (*m_iter)->annotations_.find("go.tag");
if (it != (*m_iter)->annotations_.end()) {
parse_go_tags(&tags, it->second.back());
}
string gotag;
for (auto it = tags.begin(); it != tags.end(); ++it) {
gotag += it->first + ":\"" + it->second + "\" ";
}
// Trailing whitespace
gotag.resize(gotag.size()-1);
generate_deprecation_comment(out, (*m_iter)->annotations_);
indent(out) << publicize((*m_iter)->get_name()) << " " << goType << " `thrift:\""
<< escape_string((*m_iter)->get_name()) << "," << sorted_keys_pos;
if ((*m_iter)->get_req() == t_field::T_REQUIRED) {
out << ",required";
}
out << "\" " << gotag << "`" << '\n';
sorted_keys_pos++;
}
} else {
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
generate_deprecation_comment(out, (*m_iter)->annotations_);
// This fills in default values, as opposed to nulls
out << indent() << publicize((*m_iter)->get_name()) << " "
<< type_to_go_type((*m_iter)->get_type()) << '\n';
}
}
indent_down();
out << indent() << "}" << '\n' << '\n';
generate_deprecation_comment(out, tstruct->annotations_);
out << indent() << "func New" << tstruct_name << "() *" << tstruct_name << " {" << '\n';
indent_up();
out << indent() << "return &";
generate_go_struct_initializer(out, tstruct, is_result || is_args);
indent_down();
out << indent() << "}" << '\n' << '\n';
// Default values for optional fields
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
string publicized_name;
t_const_value* def_value;
get_publicized_name_and_def_value(*m_iter, &publicized_name, &def_value);
t_type* fieldType = (*m_iter)->get_type();
string goType = type_to_go_type_with_opt(fieldType, false);
string def_var_name = tstruct_name + "_" + publicized_name + "_DEFAULT";
if ((*m_iter)->get_req() == t_field::T_OPTIONAL || is_pointer_field(*m_iter)) {
generate_deprecation_comment(out, (*m_iter)->annotations_);
out << indent() << "var " << def_var_name << " " << goType;
if (def_value != nullptr) {
out << " = " << render_const_value(fieldType, def_value, (*m_iter)->get_name());
}
out << '\n';
}
out << '\n';
// num_setable is used for deciding if Count* methods will be generated for union fields.
// This applies to all nullable fields including slices (used for set, list and binary) and maps, not just pointers.
t_type* type = fieldType->get_true_type();
if (is_pointer_field(*m_iter)|| type->is_map() || type->is_set() || type->is_list() || type->is_binary()) {
num_setable += 1;
}
if (is_pointer_field(*m_iter)) {
string goOptType = type_to_go_type_with_opt(fieldType, true);
string maybepointer = goOptType != goType ? "*" : "";
generate_deprecation_comment(out, (*m_iter)->annotations_);
out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() "
<< goType << " {" << '\n';
indent_up();
out << indent() << "if !p.IsSet" << publicized_name << "() {" << '\n';
indent_up();
out << indent() << "return " << def_var_name << '\n';
indent_down();
out << indent() << "}" << '\n';
out << indent() << "return " << maybepointer << "p." << publicized_name << '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
} else {
out << '\n';
generate_deprecation_comment(out, (*m_iter)->annotations_);
out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() "
<< goType << " {" << '\n';
indent_up();
out << indent() << "return p." << publicized_name << '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
}
}
if (tstruct->is_union() && num_setable > 0) {
generate_countsetfields_helper(out, tstruct, tstruct_name, is_result);
}
generate_isset_helpers(out, tstruct, tstruct_name, is_result);
generate_go_struct_reader(out, tstruct, tstruct_name, is_result);
generate_go_struct_writer(out, tstruct, tstruct_name, is_result, num_setable > 0);
if (!is_result && !is_args) {
generate_go_struct_equals(out, tstruct, tstruct_name);
}
out << indent() << "func (p *" << tstruct_name << ") String() string {" << '\n';
indent_up();
out << indent() << "if p == nil {" << '\n';
indent_up();
out << indent() << "return \"<nil>\"" << '\n';
indent_down();
out << indent() << "}" << '\n';
out << indent() << "return fmt.Sprintf(\"" << escape_string(tstruct_name) << "(%+v)\", *p)"
<< '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
if (is_exception) {
out << indent() << "func (p *" << tstruct_name << ") Error() string {" << '\n';
indent_up();
out << indent() << "return p.String()" << '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
out << indent() << "func (" << tstruct_name << ") TExceptionType() thrift.TExceptionType {" << '\n';
indent_up();
out << indent() << "return thrift.TExceptionTypeCompiled" << '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
out << indent() << "var _ thrift.TException = (*" << tstruct_name << ")(nil)"
<< '\n' << '\n';
}
if (!read_write_private_) {
// Generate the implementation of slog.LogValuer,
// see: https://issues.apache.org/jira/browse/THRIFT-5745
out << indent() << "func (p *" << tstruct_name << ") LogValue() slog.Value {" << '\n';
indent_up();
out << indent() << "if p == nil {" << '\n';
indent_up();
out << indent() << "return slog.AnyValue(nil)" << '\n';
indent_down();
out << indent() << "}" << '\n';
out << indent() << "v := thrift.SlogTStructWrapper{" << '\n';
indent_up();
out << indent() << "Type: \"*" << package_name_ << "." << tstruct_name << "\"," << '\n';
out << indent() << "Value: p," << '\n';
indent_down();
out << indent() << "}" << '\n';
out << indent() << "return slog.AnyValue(v)" << '\n';
indent_down();
out << indent() << "}" << '\n' << '\n';
out << indent() << "var _ slog.LogValuer = (*" << tstruct_name << ")(nil)"
<< '\n' << '\n';
}
}