void readStruct()

in lib/d/src/thrift/codegen/base.d [594:773]


void readStruct(T, Protocol, alias fieldMetaData = cast(TFieldMeta[])null,
  bool pointerStruct = false)(auto ref T s, Protocol p) if (isTProtocol!Protocol)
{
  mixin({
    string code;

    // Check that all fields for which there is meta info are actually in the
    // passed struct type.
    foreach (field; mergeFieldMeta!(T, fieldMetaData)) {
      code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n";
    }

    // Returns the code string for reading a value of type F off the wire and
    // assigning it to v. The level parameter is used to make sure that there
    // are no conflicting variable names on recursive calls.
    string readValueCode(ValueType)(string v, size_t level = 0) {
      // Some non-ambigous names to use (shadowing is not allowed in D).
      immutable i = "i" ~ to!string(level);
      immutable elem = "elem" ~ to!string(level);
      immutable key = "key" ~ to!string(level);
      immutable list = "list" ~ to!string(level);
      immutable map = "map" ~ to!string(level);
      immutable set = "set" ~ to!string(level);
      immutable value = "value" ~ to!string(level);

      alias FullyUnqual!ValueType F;

      static if (is(F == bool)) {
        return v ~ " = p.readBool();";
      } else static if (is(F == byte)) {
        return v ~ " = p.readByte();";
      } else static if (is(F == double)) {
        return v ~ " = p.readDouble();";
      } else static if (is(F == short)) {
        return v ~ " = p.readI16();";
      } else static if (is(F == int)) {
        return v ~ " = p.readI32();";
      } else static if (is(F == long)) {
        return v ~ " = p.readI64();";
      } else static if (is(F : string)) {
        return v ~ " = p.readString();";
      } else static if (is(F == enum)) {
        return v ~ " = cast(typeof(" ~ v ~ "))p.readI32();";
      } else static if (is(F _ : E[], E)) {
        return "{\n" ~
          "auto " ~ list ~ " = p.readListBegin();\n" ~
          // TODO: Check element type here?
          v ~ " = new typeof(" ~ v ~ "[0])[" ~ list ~ ".size];\n" ~
          "foreach (" ~ i ~ "; 0 .. " ~ list ~ ".size) {\n" ~
            readValueCode!E(v ~ "[" ~ i ~ "]", level + 1) ~ "\n" ~
          "}\n" ~
          "p.readListEnd();\n" ~
        "}";
      } else static if (is(F _ : V[K], K, V)) {
        return "{\n" ~
          "auto " ~ map ~ " = p.readMapBegin();" ~
          v ~ " = null;\n" ~
          // TODO: Check key/value types here?
          "foreach (" ~ i ~ "; 0 .. " ~ map ~ ".size) {\n" ~
            "FullyUnqual!(typeof(" ~ v ~ ".keys[0])) " ~ key ~ ";\n" ~
            readValueCode!K(key, level + 1) ~ "\n" ~
            "typeof(" ~ v ~ ".values[0]) " ~ value ~ ";\n" ~
            readValueCode!V(value, level + 1) ~ "\n" ~
            v ~ "[cast(typeof(" ~ v ~ ".keys[0]))" ~ key ~ "] = " ~ value ~ ";\n" ~
          "}\n" ~
          "p.readMapEnd();" ~
        "}";
      } else static if (is(F _ : HashSet!(E), E)) {
        return "{\n" ~
          "auto " ~ set ~ " = p.readSetBegin();" ~
          // TODO: Check element type here?
          v ~ " = new typeof(" ~ v ~ ")();\n" ~
          "foreach (" ~ i ~ "; 0 .. " ~ set ~ ".size) {\n" ~
            "typeof(" ~ v ~ "[][0]) " ~ elem ~ ";\n" ~
            readValueCode!E(elem, level + 1) ~ "\n" ~
            v ~ " ~= " ~ elem ~ ";\n" ~
          "}\n" ~
          "p.readSetEnd();" ~
        "}";
      } else static if (is(F == struct) || is(F : TException)) {
        static if (is(F == struct)) {
          auto result = v ~ " = typeof(" ~ v ~ ")();\n";
        } else {
          auto result = v ~ " = new typeof(" ~ v ~ ")();\n";
        }

        static if (__traits(compiles, F.init.read(TProtocol.init))) {
          result ~= v ~ ".read(p);";
        } else {
          result ~= "readStruct(" ~ v ~ ", p);";
        }
        return result;
      } else {
        static assert(false, "Cannot represent type in Thrift: " ~ F.stringof);
      }
    }

    string readFieldCode(FieldType)(string name, short id, TReq req) {
      static if (pointerStruct && isPointer!FieldType) {
        immutable v = "(*s." ~ name ~ ")";
        alias PointerTarget!FieldType F;
      } else {
        immutable v = "s." ~ name;
        alias FieldType F;
      }

      string code = "case " ~ to!string(id) ~ ":\n";
      code ~= "if (f.type == " ~ dToTTypeString!F ~ ") {\n";
      code ~= readValueCode!F(v) ~ "\n";
      if (req == TReq.REQUIRED) {
        // For required fields, set the corresponding local isSet variable.
        code ~= "isSet_" ~ name ~ " = true;\n";
      } else if (!isNullable!F){
        code ~= "s.isSetFlags." ~ name ~ " = true;\n";
      }
      code ~= "} else skip(p, f.type);\n";
      code ~= "break;\n";
      return code;
    }

    // Code for the local boolean flags used to make sure required fields have
    // been found.
    string isSetFlagCode = "";

    // Code for checking whether the flags for the required fields are true.
    string isSetCheckCode = "";

    /// Code for the case statements storing the fields to the result struct.
    string readMembersCode = "";

    // The last automatically assigned id – fields with no meta information
    // are assigned (in lexical order) descending negative ids, starting with
    // -1, just like the Thrift compiler does.
    short lastId;

    foreach (name; FieldNames!T) {
      enum req = memberReq!(T, name, fieldMetaData);
      if (req == TReq.REQUIRED) {
        // For required fields, generate local bool flags to keep track
        // whether the field has been encountered.
        immutable n = "isSet_" ~ name;
        isSetFlagCode ~= "bool " ~ n ~ ";\n";
        isSetCheckCode ~= "enforce(" ~ n ~ ", new TProtocolException(" ~
          "`Required field '" ~ name ~ "' not found in serialized data`, " ~
          "TProtocolException.Type.INVALID_DATA));\n";
      }

      enum meta = find!`a.name == b`(mergeFieldMeta!(T, fieldMetaData), name);
      static if (meta.empty) {
        --lastId;
        version (TVerboseCodegen) {
          code ~= "pragma(msg, `[thrift.codegen.base.readStruct] Warning: No " ~
            "meta information for field '" ~ name ~ "' in struct '" ~
            T.stringof ~ "'. Assigned id: " ~ to!string(lastId) ~ ".`);\n";
        }
        readMembersCode ~= readFieldCode!(MemberType!(T, name))(
          name, lastId, req);
      } else static if (req != TReq.IGNORE) {
        readMembersCode ~= readFieldCode!(MemberType!(T, name))(
          name, meta.front.id, req);
      }
    }

    code ~= isSetFlagCode;
    code ~= "p.readStructBegin();\n";
    code ~= "while (true) {\n";
    code ~= "auto f = p.readFieldBegin();\n";
    code ~= "if (f.type == TType.STOP) break;\n";
    code ~= "switch(f.id) {\n";
    code ~= readMembersCode;
    code ~= "default: skip(p, f.type);\n";
    code ~= "}\n";
    code ~= "p.readFieldEnd();\n";
    code ~= "}\n";
    code ~= "p.readStructEnd();\n";
    code ~= isSetCheckCode;

    return code;
  }());
}