Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change data serialization methods to auto ref in #2275

Merged
merged 5 commits into from
Jun 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 65 additions & 51 deletions data/vibe/data/bson.d
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ struct BsonRegex {

See_Also: `deserializeBson`
*/
Bson serializeToBson(T)(T value, ubyte[] buffer = null)
Bson serializeToBson(T)(auto ref T value, ubyte[] buffer = null)
{
return serialize!BsonSerializer(value, buffer);
}
Expand Down Expand Up @@ -1229,19 +1229,19 @@ unittest {
@safe unittest {
static struct A { int value; static A fromJson(Json val) @safe { return A(val.get!int); } Json toJson() const @safe { return Json(value); } Bson toBson() { return Bson(); } }
static assert(!isStringSerializable!A && isJsonSerializable!A && !isBsonSerializable!A);
static assert(!isStringSerializable!(const(A)) && !isJsonSerializable!(const(A)) && !isBsonSerializable!(const(A)));
static assert(!isStringSerializable!(const(A)) && isJsonSerializable!(const(A)) && !isBsonSerializable!(const(A)));
tchaloupka marked this conversation as resolved.
Show resolved Hide resolved
// assert(serializeToBson(const A(123)) == Bson(123));
// assert(serializeToBson(A(123)) == Bson(123));

static struct B { int value; static B fromBson(Bson val) @safe { return B(val.get!int); } Bson toBson() const @safe { return Bson(value); } Json toJson() { return Json(); } }
static assert(!isStringSerializable!B && !isJsonSerializable!B && isBsonSerializable!B);
static assert(!isStringSerializable!(const(B)) && !isJsonSerializable!(const(B)) && !isBsonSerializable!(const(B)));
static assert(!isStringSerializable!(const(B)) && !isJsonSerializable!(const(B)) && isBsonSerializable!(const(B)));
assert(serializeToBson(const B(123)) == Bson(123));
assert(serializeToBson(B(123)) == Bson(123));

static struct C { int value; static C fromString(string val) @safe { return C(val.to!int); } string toString() const @safe { return value.to!string; } Json toJson() { return Json(); } }
static assert(isStringSerializable!C && !isJsonSerializable!C && !isBsonSerializable!C);
static assert(!isStringSerializable!(const(C)) && !isJsonSerializable!(const(C)) && !isBsonSerializable!(const(C)));
static assert(isStringSerializable!(const(C)) && !isJsonSerializable!(const(C)) && !isBsonSerializable!(const(C)));
assert(serializeToBson(const C(123)) == Bson("123"));
assert(serializeToBson(C(123)) == Bson("123"));

Expand All @@ -1254,7 +1254,7 @@ unittest {
// test if const(class) is serializable
static class E { int value; this(int v) @safe { value = v; } static E fromBson(Bson val) @safe { return new E(val.get!int); } Bson toBson() const @safe { return Bson(value); } Json toJson() { return Json(); } }
static assert(!isStringSerializable!E && !isJsonSerializable!E && isBsonSerializable!E);
static assert(!isStringSerializable!(const(E)) && !isJsonSerializable!(const(E)) && !isBsonSerializable!(const(E)));
static assert(!isStringSerializable!(const(E)) && !isJsonSerializable!(const(E)) && isBsonSerializable!(const(E)));
assert(serializeToBson(new const E(123)) == Bson(123));
assert(serializeToBson(new E(123)) == Bson(123));
}
Expand Down Expand Up @@ -1466,38 +1466,38 @@ struct BsonSerializer {
void beginWriteArrayEntry(Traits)(size_t idx) { m_entryIndex = idx; }
void endWriteArrayEntry(Traits)(size_t idx) {}

// auto ref does't work for DMD 2.064
void writeValue(Traits, T)(/*auto ref const*/ in T value) { writeValueH!(T, true)(value); }
void writeValue(Traits, T)(auto ref T value) { writeValueH!(T, true)(value); }

private void writeValueH(T, bool write_header)(/*auto ref const*/ in T value)
private void writeValueH(T, bool write_header)(auto ref T value)
{
alias UT = Unqual!T;
static if (write_header) writeCompositeEntryHeader(getBsonTypeID(value));

static if (is(T == Bson)) { m_dst.put(value.data); }
else static if (is(T == Json)) { m_dst.put(Bson(value).data); } // FIXME: use .writeBsonValue
else static if (is(T == typeof(null))) {}
else static if (is(T == string)) { m_dst.put(toBsonData(cast(uint)value.length+1)); m_dst.putCString(value); }
else static if (is(T == BsonBinData)) { m_dst.put(toBsonData(cast(int)value.rawData.length)); m_dst.put(value.type); m_dst.put(value.rawData); }
else static if (is(T == BsonObjectID)) { m_dst.put(value.m_bytes[]); }
else static if (is(T == BsonDate)) { m_dst.put(toBsonData(value.m_time)); }
else static if (is(T == SysTime)) { m_dst.put(toBsonData(BsonDate(value).m_time)); }
else static if (is(T == BsonRegex)) { m_dst.putCString(value.expression); m_dst.putCString(value.options); }
else static if (is(T == BsonTimestamp)) { m_dst.put(toBsonData(value.m_time)); }
else static if (is(T == bool)) { m_dst.put(cast(ubyte)(value ? 0x01 : 0x00)); }
else static if (is(T : int) && isIntegral!T) { m_dst.put(toBsonData(cast(int)value)); }
else static if (is(T : long) && isIntegral!T) { m_dst.put(toBsonData(value)); }
else static if (is(T : double) && isFloatingPoint!T) { m_dst.put(toBsonData(cast(double)value)); }
else static if (is(T == UUID)) { m_dst.put(Bson(value).data); }
else static if (isBsonSerializable!T) {
static if (is(UT == Bson)) { m_dst.put(value.data); }
else static if (is(UT == Json)) { m_dst.put(Bson(value).data); } // FIXME: use .writeBsonValue
else static if (is(UT == typeof(null))) {}
else static if (is(UT == string)) { m_dst.put(toBsonData(cast(uint)value.length+1)); m_dst.putCString(value); }
else static if (is(UT == BsonBinData)) { m_dst.put(toBsonData(cast(int)value.rawData.length)); m_dst.put(value.type); m_dst.put(value.rawData); }
else static if (is(UT == BsonObjectID)) { m_dst.put(value.m_bytes[]); }
else static if (is(UT == BsonDate)) { m_dst.put(toBsonData(value.m_time)); }
else static if (is(UT == SysTime)) { m_dst.put(toBsonData(BsonDate(value).m_time)); }
else static if (is(UT == BsonRegex)) { m_dst.putCString(value.expression); m_dst.putCString(value.options); }
else static if (is(UT == BsonTimestamp)) { m_dst.put(toBsonData(value.m_time)); }
else static if (is(UT == bool)) { m_dst.put(cast(ubyte)(value ? 0x01 : 0x00)); }
else static if (is(UT : int) && isIntegral!UT) { m_dst.put(toBsonData(cast(int)value)); }
else static if (is(UT : long) && isIntegral!UT) { m_dst.put(toBsonData(value)); }
else static if (is(UT : double) && isFloatingPoint!UT) { m_dst.put(toBsonData(cast(double)value)); }
else static if (is(UT == UUID)) { m_dst.put(Bson(value).data); }
else static if (isBsonSerializable!UT) {
static if (!__traits(compiles, () @safe { return value.toBson(); } ()))
pragma(msg, "Non-@safe toBson/fromBson methods are deprecated - annotate "~T.stringof~".toBson() with @safe.");
m_dst.put(() @trusted { return value.toBson(); } ().data);
} else static if (isJsonSerializable!T) {
} else static if (isJsonSerializable!UT) {
static if (!__traits(compiles, () @safe { return value.toJson(); } ()))
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".toJson() with @safe.");
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe.");
m_dst.put(Bson(() @trusted { return value.toJson(); } ()).data);
} else static if (is(T : const(ubyte)[])) { writeValueH!(BsonBinData, false)(BsonBinData(BsonBinData.Type.generic, value.idup)); }
else static assert(false, "Unsupported type: " ~ T.stringof);
} else static if (is(UT : const(ubyte)[])) { writeValueH!(BsonBinData, false)(BsonBinData(BsonBinData.Type.generic, value.idup)); }
else static assert(false, "Unsupported type: " ~ UT.stringof);
}

private void writeCompositeEntryHeader(Bson.Type tp)
Expand Down Expand Up @@ -1604,31 +1604,32 @@ struct BsonSerializer {
return false;
}

private static Bson.Type getBsonTypeID(T, bool accept_ao = false)(/*auto ref const*/ in T value)
private static Bson.Type getBsonTypeID(T, bool accept_ao = false)(auto ref T value)
@safe {
alias UT = Unqual!T;
Bson.Type tp;
static if (is(T == Bson)) tp = value.type;
else static if (is(T == Json)) tp = jsonTypeToBsonType(value.type);
else static if (is(T == typeof(null))) tp = Bson.Type.null_;
else static if (is(T == string)) tp = Bson.Type.string;
else static if (is(T == BsonBinData)) tp = Bson.Type.binData;
else static if (is(T == BsonObjectID)) tp = Bson.Type.objectID;
else static if (is(T == BsonDate)) tp = Bson.Type.date;
else static if (is(T == SysTime)) tp = Bson.Type.date;
else static if (is(T == BsonRegex)) tp = Bson.Type.regex;
else static if (is(T == BsonTimestamp)) tp = Bson.Type.timestamp;
else static if (is(T == bool)) tp = Bson.Type.bool_;
else static if (isIntegral!T && is(T : int)) tp = Bson.Type.int_;
else static if (isIntegral!T && is(T : long)) tp = Bson.Type.long_;
else static if (isFloatingPoint!T && is(T : double)) tp = Bson.Type.double_;
else static if (isBsonSerializable!T) tp = value.toBson().type; // FIXME: this is highly inefficient
else static if (isJsonSerializable!T) tp = jsonTypeToBsonType(value.toJson().type); // FIXME: this is highly inefficient
else static if (is(T == UUID)) tp = Bson.Type.binData;
else static if (is(T : const(ubyte)[])) tp = Bson.Type.binData;
else static if (accept_ao && isArray!T) tp = Bson.Type.array;
else static if (accept_ao && isAssociativeArray!T) tp = Bson.Type.object;
else static if (accept_ao && (is(T == class) || is(T == struct))) tp = Bson.Type.object;
else static assert(false, "Unsupported type: " ~ T.stringof);
else static if (is(UT == Json)) tp = jsonTypeToBsonType(value.type);
else static if (is(UT == typeof(null))) tp = Bson.Type.null_;
else static if (is(UT == string)) tp = Bson.Type.string;
else static if (is(UT == BsonBinData)) tp = Bson.Type.binData;
else static if (is(UT == BsonObjectID)) tp = Bson.Type.objectID;
else static if (is(UT == BsonDate)) tp = Bson.Type.date;
else static if (is(UT == SysTime)) tp = Bson.Type.date;
else static if (is(UT == BsonRegex)) tp = Bson.Type.regex;
else static if (is(UT == BsonTimestamp)) tp = Bson.Type.timestamp;
else static if (is(UT == bool)) tp = Bson.Type.bool_;
else static if (isIntegral!UT && is(UT : int)) tp = Bson.Type.int_;
else static if (isIntegral!UT && is(UT : long)) tp = Bson.Type.long_;
else static if (isFloatingPoint!UT && is(UT : double)) tp = Bson.Type.double_;
else static if (isBsonSerializable!UT) tp = value.toBson().type; // FIXME: this is highly inefficient
else static if (isJsonSerializable!UT) tp = jsonTypeToBsonType(value.toJson().type); // FIXME: this is highly inefficient
else static if (is(UT == UUID)) tp = Bson.Type.binData;
else static if (is(UT : const(ubyte)[])) tp = Bson.Type.binData;
else static if (accept_ao && isArray!UT) tp = Bson.Type.array;
else static if (accept_ao && isAssociativeArray!UT) tp = Bson.Type.object;
else static if (accept_ao && (is(UT == class) || is(UT == struct))) tp = Bson.Type.object;
else static assert(false, "Unsupported type: " ~ UT.stringof);
return tp;
}
}
Expand Down Expand Up @@ -1713,6 +1714,19 @@ unittest
assert(serializeToBson(jsvalue).toJson() == jsvalue);
}

unittest
{
static struct Pipeline(ARGS...) { @asArray ARGS pipeline; }
auto getPipeline(ARGS...)(ARGS args) { return Pipeline!ARGS(args); }

string[string] a = ["foo":"bar"];
int b = 42;

auto fields = getPipeline(a, b).serializeToBson()["pipeline"].get!(Bson[]);
assert(fields[0]["foo"].get!string == "bar");
assert(fields[1].get!int == 42);
}

private string skipCString(ref bdata_t data)
@safe {
auto idx = data.countUntil(0);
Expand Down Expand Up @@ -1766,4 +1780,4 @@ pure @safe {
}

/// private
package template isBsonSerializable(T) { enum isBsonSerializable = is(typeof(T.init.toBson()) == Bson) && is(typeof(T.fromBson(Bson())) == T); }
package template isBsonSerializable(T) { enum isBsonSerializable = is(typeof(T.init.toBson()) : Bson) && is(typeof(T.fromBson(Bson())) : T); }
66 changes: 37 additions & 29 deletions data/vibe/data/json.d
Original file line number Diff line number Diff line change
Expand Up @@ -1433,18 +1433,18 @@ Json parseJsonString(string str, string filename = null)

See_Also: `deserializeJson`, `vibe.data.serialization`
*/
Json serializeToJson(T)(T value)
Json serializeToJson(T)(auto ref T value)
{
return serialize!JsonSerializer(value);
}
/// ditto
void serializeToJson(R, T)(R destination, T value)
void serializeToJson(R, T)(R destination, auto ref T value)
if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
{
serialize!(JsonStringSerializer!R)(value, destination);
}
/// ditto
string serializeToJsonString(T)(T value)
string serializeToJsonString(T)(auto ref T value)
{
auto ret = appender!string;
serializeToJson(ret, value);
Expand Down Expand Up @@ -1477,13 +1477,13 @@ string serializeToJsonString(T)(T value)

See_also: `serializeToJson`, `vibe.data.serialization`
*/
void serializeToPrettyJson(R, T)(R destination, T value)
void serializeToPrettyJson(R, T)(R destination, auto ref T value)
if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
{
serialize!(JsonStringSerializer!(R, true))(value, destination);
}
/// ditto
string serializeToPrettyJson(T)(T value)
string serializeToPrettyJson(T)(auto ref T value)
{
auto ret = appender!string;
serializeToPrettyJson(ret, value);
Expand Down Expand Up @@ -1752,9 +1752,9 @@ unittest {
See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson
*/
struct JsonSerializer {
template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(T == UUID) || isJsonSerializable!T; }
template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJsonSerializable!T; }

template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json) || is (T == JSONValue); }
template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(Unqual!T == Json) || is(Unqual!T == JSONValue); }

private {
Json m_current;
Expand All @@ -1779,22 +1779,23 @@ struct JsonSerializer {
void beginWriteArrayEntry(Traits)(size_t) {}
void endWriteArrayEntry(Traits)(size_t) { m_compositeStack[$-1].appendArrayElement(m_current); }

void writeValue(Traits, T)(in T value)
if (!is(T == Json))
void writeValue(Traits, T)(auto ref T value)
if (!is(Unqual!T == Json))
{
static if (is(T == JSONValue)) {
alias UT = Unqual!T;
static if (is(UT == JSONValue)) {
m_current = Json(value);
} else static if (isJsonSerializable!T) {
} else static if (isJsonSerializable!UT) {
static if (!__traits(compiles, () @safe { return value.toJson(); } ()))
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".toJson() with @safe.");
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe.");
m_current = () @trusted { return value.toJson(); } ();
} else static if (isSomeString!T && !is(T == string)) {
} else static if (isSomeString!T && !is(UT == string)) {
writeValue!Traits(value.to!string);
} else m_current = Json(value);
}

void writeValue(Traits, T)(Json value) if (is(T == Json)) { m_current = value; }
void writeValue(Traits, T)(in Json value) if (is(T == Json)) { m_current = value.clone; }
void writeValue(Traits, T)(auto ref T value) if (is(T == Json)) { m_current = value; }
void writeValue(Traits, T)(auto ref T value) if (!is(T == Json) && is(T : const(Json))) { m_current = value.clone; }

//
// deserialization
Expand Down Expand Up @@ -1877,6 +1878,12 @@ unittest {
assert(obj.deserializeJson!T.a == "");
}

unittest {
class C { this(Json j) {foo = j;} Json foo; }
const C c = new C(Json(42));
assert(serializeToJson(c)["foo"].get!int == 42);
}

/**
Serializer for a range based plain JSON string representation.

Expand All @@ -1890,9 +1897,9 @@ struct JsonStringSerializer(R, bool pretty = false)
size_t m_level = 0;
}

template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(T == UUID) || isJsonSerializable!T; }
template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJsonSerializable!T; }

template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json) || is(T == JSONValue); }
template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!(Unqual!T) || is(Unqual!T == Json) || is(Unqual!T == JSONValue); }

this(R range)
{
Expand Down Expand Up @@ -1930,24 +1937,25 @@ struct JsonStringSerializer(R, bool pretty = false)

void writeValue(Traits, T)(in T value)
{
alias UT = Unqual!T;
static if (is(T == typeof(null))) m_range.put("null");
else static if (is(T == bool)) m_range.put(value ? "true" : "false");
else static if (is(T : long)) m_range.formattedWrite("%s", value);
else static if (is(T == BigInt)) () @trusted { m_range.formattedWrite("%d", value); } ();
else static if (is(T : real)) value == value ? m_range.formattedWrite("%.16g", value) : m_range.put("null");
else static if (is(T : const(char)[])) {
else static if (is(UT == bool)) m_range.put(value ? "true" : "false");
else static if (is(UT : long)) m_range.formattedWrite("%s", value);
else static if (is(UT == BigInt)) () @trusted { m_range.formattedWrite("%d", value); } ();
else static if (is(UT : real)) value == value ? m_range.formattedWrite("%.16g", value) : m_range.put("null");
else static if (is(UT : const(char)[])) {
m_range.put('"');
m_range.jsonEscape(value);
m_range.put('"');
} else static if (isSomeString!T) writeValue!Traits(value.to!string); // TODO: avoid memory allocation
else static if (is(T == UUID)) writeValue!Traits(value.toString());
else static if (is(T == Json)) m_range.writeJsonString(value);
else static if (is(T == JSONValue)) m_range.writeJsonString(Json(value));
else static if (isJsonSerializable!T) {
else static if (is(UT == UUID)) writeValue!Traits(value.toString());
else static if (is(UT == Json)) m_range.writeJsonString(value);
else static if (is(UT == JSONValue)) m_range.writeJsonString(Json(value));
else static if (isJsonSerializable!UT) {
static if (!__traits(compiles, () @safe { return value.toJson(); } ()))
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".toJson() with @safe.");
pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe.");
m_range.writeJsonString!(R, pretty)(() @trusted { return value.toJson(); } (), m_level);
} else static assert(false, "Unsupported type: " ~ T.stringof);
} else static assert(false, "Unsupported type: " ~ UT.stringof);
}

private void startComposite()
Expand Down Expand Up @@ -2586,7 +2594,7 @@ private string underscoreStrip(string field_name)
}

/// private
package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) == Json) && is(typeof(T.fromJson(Json())) == T); }
package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) : Json) && is(typeof(T.fromJson(Json())) : T); }

private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception")
{
Expand Down
Loading