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;
}());
}