void t_go_generator::generate_go_struct_reader()

in thrift/compiler/generate/t_go_generator.cc [1839:1995]


void t_go_generator::generate_go_struct_reader(
    ofstream& out,
    const t_struct* tstruct,
    const string& tstruct_name,
    bool is_result) {
  (void)is_result;
  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;
  string escaped_tstruct_name(escape_string(tstruct->get_name()));
  out << indent() << "func (p *" << tstruct_name
      << ") Read(iprot thrift.Protocol) error {" << endl;
  indent_up();
  out << indent() << "if _, err := iprot.ReadStructBegin(); err != nil {"
      << endl;
  out << indent()
      << "  return thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)"
      << endl;
  out << indent() << "}" << endl << endl;

  // Required variables does not have IsSet functions, so we need tmp vars to
  // check them.
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if ((*f_iter)->get_req() == t_field::e_req::required) {
      const string field_name(publicize(escape_string((*f_iter)->get_name())));
      indent(out) << "var isset" << field_name << " bool = false;" << endl;
    }
  }
  out << endl;

  // Loop over reading in fields
  indent(out) << "for {" << endl;
  indent_up();
  // Read beginning field marker
  out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()"
      << endl;
  out << indent() << "if err != nil {" << endl;
  indent_up();
  out << indent()
      << "return thrift.PrependError(fmt.Sprintf("
         "\"%T field %d read error: \", p, fieldId), err)"
      << endl;
  indent_down();
  out << indent() << "}" << endl;
  // Check for field STOP marker and break
  out << indent() << "if fieldTypeId == thrift.STOP { break; }" << endl;

  string thriftFieldTypeId;
  // Generate deserialization code for known cases
  set<int32_t> seen;

  // Switch statement on the field we are reading, false if no fields present
  bool have_switch = !fields.empty();
  if (have_switch) {
    indent(out) << "switch fieldId {" << endl;
  }

  // All the fields we know
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    int32_t field_id = (*f_iter)->get_key();

    // -1 -> ReadField_1, 1 -> ReadField1
    string field_method("ReadField");
    field_method += (field_id < 0 ? "_" : "");
    field_method += std::to_string(std::abs(field_id));

    if (seen.find(field_id) != seen.end()) {
      continue;
    }
    seen.insert(field_id);

    out << indent() << "case " << field_id << ":" << endl;
    indent_up();
    thriftFieldTypeId = type_to_enum((*f_iter)->get_type());

    if (thriftFieldTypeId == "thrift.BINARY") {
      thriftFieldTypeId = "thrift.STRING";
    }

    out << indent() << "if err := p." << field_method << "(iprot); err != nil {"
        << endl;
    out << indent() << "  return err" << endl;
    out << indent() << "}" << endl;

    // Mark required field as read
    if ((*f_iter)->get_req() == t_field::e_req::required) {
      const string field_name(publicize(escape_string((*f_iter)->get_name())));
      out << indent() << "isset" << field_name << " = true" << endl;
    }

    indent_down();
  }

  // Begin switch default case
  if (have_switch) {
    out << indent() << "default:" << endl;
    indent_up();
  }

  // Skip unknown fields in either case
  out << indent() << "if err := iprot.Skip(fieldTypeId); err != nil {" << endl;
  out << indent() << "  return err" << endl;
  out << indent() << "}" << endl;

  // End switch default case
  if (have_switch) {
    indent_down();
    out << indent() << "}" << endl;
  }

  // Read field end marker
  out << indent() << "if err := iprot.ReadFieldEnd(); err != nil {" << endl;
  out << indent() << "  return err" << endl;
  out << indent() << "}" << endl;
  indent_down();
  out << indent() << "}" << endl;
  out << indent() << "if err := iprot.ReadStructEnd(); err != nil {" << endl;
  out << indent()
      << "  return thrift.PrependError(fmt.Sprintf("
         "\"%T read struct end error: \", p), err)"
      << endl;
  out << indent() << "}" << endl;

  // Return error if any required fields are missing.
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if ((*f_iter)->get_req() == t_field::e_req::required) {
      const string field_name(publicize(escape_string((*f_iter)->get_name())));
      out << indent() << "if !isset" << field_name << "{" << endl;
      out << indent()
          << "  return thrift.NewProtocolExceptionWithType(thrift.INVALID_DATA, "
             "fmt.Errorf(\"Required field "
          << field_name << " is not set\"));" << endl;
      out << indent() << "}" << endl;
    }
  }

  out << indent() << "return nil" << endl;
  indent_down();
  out << indent() << "}" << endl << endl;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    string field_type_name(publicize((*f_iter)->get_type()->get_name()));
    string field_name(publicize((*f_iter)->get_name()));
    int32_t field_id = (*f_iter)->get_key();
    // -1 -> ReadField_1, 1 -> ReadField1
    string field_method("ReadField");
    field_method += (field_id < 0 ? "_" : "");
    field_method += std::to_string(std::abs(field_id));

    out << indent() << "func (p *" << tstruct_name << ")  " << field_method
        << "(iprot thrift.Protocol) error {" << endl;
    indent_up();
    generate_deserialize_field(out, *f_iter, false, "p.");
    indent_down();
    out << indent() << "  return nil" << endl;
    out << indent() << "}" << endl << endl;
  }
}