Skip to content

Commit

Permalink
Merge pull request #149 from tchaloupka/nullable_geo
Browse files Browse the repository at this point in the history
Resolve of Nullable.get warnings
  • Loading branch information
denizzzka authored Sep 25, 2020
2 parents 59f5be9 + f6be592 commit c3d2824
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 26 deletions.
16 changes: 14 additions & 2 deletions src/dpq2/conv/from_d_types.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module dpq2.conv.from_d_types;
@safe:

public import dpq2.conv.arrays : isArrayType, toValue, isStaticArrayString;
public import dpq2.conv.geometric : toValue;
public import dpq2.conv.geometric : isGeometricType, toValue;
import dpq2.conv.time : POSTGRES_EPOCH_DATE, TimeStamp, TimeStampUTC;
import dpq2.oids : detectOidTypeFromNative, oidConvTo, OidType;
import dpq2.value : Value, ValueFormat;
Expand All @@ -21,7 +21,7 @@ import money: currency;

/// Converts Nullable!T to Value
Value toValue(T)(T v)
if (is(T == Nullable!R, R) && !(isArrayType!(typeof(v.get))))
if (is(T == Nullable!R, R) && !(isArrayType!(typeof(v.get))) && !isGeometricType!(typeof(v.get)))
{
if (v.isNull)
return Value(ValueFormat.BINARY, detectOidTypeFromNative!T);
Expand All @@ -42,6 +42,18 @@ if (is(T == Nullable!R, R) && (isArrayType!(typeof(v.get))))
return arrToValue(v.get);
}

/// ditto
Value toValue(T)(T v)
if (is(T == Nullable!R, R) && isGeometricType!(typeof(v.get)))
{
import dpq2.conv.geometric : geoToValue = toValue; // deprecation import workaround

if (v.isNull)
return Value(ValueFormat.BINARY, detectOidTypeFromNative!T);
else
return geoToValue(v.get);
}

///
Value toValue(T)(T v)
if(isNumeric!(T))
Expand Down
72 changes: 53 additions & 19 deletions src/dpq2/conv/geometric.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dpq2.oids: OidType;
import dpq2.value: ConvExceptionType, throwTypeComplaint, Value, ValueConvException, ValueFormat;
import std.bitmanip: bigEndianToNative, nativeToBigEndian;
import std.traits;
import std.typecons : Nullable;
import std.range.primitives: ElementType;

@safe:
Expand All @@ -21,17 +22,30 @@ private template GetRvalueOfMember(T, string memberName)
alias GetRvalueOfMember = R;
}

template isGeometricType(T) if(!is(T == Nullable!N, N))
{
enum isGeometricType =
isValidPointType!T
|| isValidLineType!T
|| isValidPathType!T
|| isValidPolygon!T
|| isValidCircleType!T
|| isValidLineSegmentType!T
|| isValidBoxType!T;
}

/// Checks that type have "x" and "y" members of returning type "double"
bool isValidPointType(T)()
template isValidPointType(T)
{
static if(__traits(compiles, typeof(T.x)) && __traits(compiles, typeof(T.y)))
static if (is(T == Nullable!R, R)) enum isValidPointType = false;
else static if(__traits(compiles, typeof(T.x)) && __traits(compiles, typeof(T.y)))
{
return
enum isValidPointType =
is(GetRvalueOfMember!(T, "x") == double) &&
is(GetRvalueOfMember!(T, "y") == double);
}
else
return false;
enum isValidPointType = false;
}

unittest
Expand All @@ -48,35 +62,55 @@ unittest
}

/// Checks that type have "min" and "max" members of suitable returning type of point
bool isValidBoxType(T)()
template isValidBoxType(T)
{
static if(__traits(compiles, typeof(T.min)) && __traits(compiles, typeof(T.max)))
static if (is(T == Nullable!R, R)) enum isValidBoxType = false;
else static if(__traits(compiles, typeof(T.min)) && __traits(compiles, typeof(T.max)))
{
return
enum isValidBoxType =
isValidPointType!(GetRvalueOfMember!(T, "min")) &&
isValidPointType!(GetRvalueOfMember!(T, "max"));
}
else
return false;
enum isValidBoxType = false;
}

template isValidLineType(T)
{
enum isValidLineType = is(T == Line);
}

template isValidPathType(T)
{
enum isValidPathType = isInstanceOf!(Path, T);
}

template isValidCircleType(T)
{
enum isValidCircleType = isInstanceOf!(Circle, T);
}

///
bool isValidLineSegmentType(T)()
template isValidLineSegmentType(T)
{
static if(__traits(compiles, typeof(T.start)) && __traits(compiles, typeof(T.end)))
static if (is(T == Nullable!R, R)) enum isValidLineSegmentType = false;
else static if(__traits(compiles, typeof(T.start)) && __traits(compiles, typeof(T.end)))
{
return
enum isValidLineSegmentType =
isValidPointType!(GetRvalueOfMember!(T, "start")) &&
isValidPointType!(GetRvalueOfMember!(T, "end"));
}
else
return false;
enum isValidLineSegmentType = false;
}

///
bool isValidPolygon(T)()
template isValidPolygon(T)
{
return isArray!T && isValidPointType!(ElementType!T);
static if (is(T == Nullable!R, R))
enum isValidPolygon = false;
else
enum isValidPolygon = isArray!T && isValidPointType!(ElementType!T);
}

unittest
Expand Down Expand Up @@ -148,7 +182,7 @@ if(isValidPointType!Point)
}

Value toValue(T)(T line)
if(is(T == Line))
if(isValidLineType!T)
{
import std.algorithm : copy;

Expand All @@ -173,7 +207,7 @@ if(isValidLineSegmentType!LineSegment)
}

Value toValue(T)(T path)
if(isInstanceOf!(Path, T))
if(isValidPathType!T)
{
import std.algorithm : copy;

Expand Down Expand Up @@ -214,7 +248,7 @@ if(isValidPolygon!Polygon)
}

Value toValue(T)(T c)
if(isInstanceOf!(Circle, T))
if(isValidCircleType!T)
{
import std.algorithm : copy;

Expand Down Expand Up @@ -400,8 +434,8 @@ package mixin template GeometricInstancesForIntegrationTest()
seg2d seg;
alias seg this;

ref Point start(){ return a; }
ref Point end(){ return b; }
ref Point start() return { return a; }
ref Point end() return { return b; }

this(Point a, Point b)
{
Expand Down
25 changes: 20 additions & 5 deletions src/dpq2/conv/native_tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ public void _integration_test( string connParam ) @system
import std.algorithm : strip;
import std.string : representation;

static string formatValue(T val)
{
import std.algorithm : joiner, map, strip;
import std.conv : text, to;
import std.range : chain, ElementType;

// Nullable format deprecation workaround
static if (is(T == Nullable!R, R))
return val.isNull ? "null" : val.get.to!string;
else static if (isArrayType!T && is(ElementType!T == Nullable!E, E))
return chain("[", val.map!(a => a.isNull ? "null" : a.to!string).joiner(", "), "]").text;
else return val.to!string;
}

// test string to native conversion
params.sqlCommand = format("SELECT %s::%s as d_type_test_value", pgValue is null ? "NULL" : pgValue, pgType);
params.args = null;
Expand All @@ -69,16 +83,15 @@ public void _integration_test( string connParam ) @system
else
const bool assertResult = result == nativeValue;

assert(
assertResult,
assert(assertResult,
format("PG to native conv: received unexpected value\nreceived pgType=%s\nexpected nativeType=%s\nsent pgValue=%s\nexpected nativeValue=%s\nresult=%s",
v.oidType, typeid(T), pgValue, nativeValue, result)
v.oidType, typeid(T), pgValue, formatValue(nativeValue), formatValue(result))
);

{
// test binary to text conversion
params.sqlCommand = "SELECT $1::text";
params.args = [nativeValue.toValue];
params.args = [toValue(nativeValue)];

auto answer2 = conn.execParams(params);
auto v2 = answer2[0][0];
Expand All @@ -102,7 +115,7 @@ public void _integration_test( string connParam ) @system

assert(textResult == pgValue,
format("Native to PG conv: received unexpected value\nreceived pgType=%s\nsent nativeType=%s\nsent nativeValue=%s\nexpected pgValue=%s\nresult=%s\nexpectedRepresentation=%s\nreceivedRepresentation=%s",
v.oidType, typeid(T), nativeValue, pgValue, textResult, pgValue.representation, textResult.representation)
v.oidType, typeid(T), formatValue(nativeValue), pgValue, textResult, pgValue.representation, textResult.representation)
);
}
}
Expand Down Expand Up @@ -186,6 +199,7 @@ public void _integration_test( string connParam ) @system
// SysTime testing
auto testTZ = new immutable SimpleTimeZone(2.dur!"hours"); // custom TZ
C!SysTime(SysTime(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12), testTZ), "timestamptz", "'1997-12-17 07:37:16.000012+02'");
C!(Nullable!SysTime)(Nullable!SysTime(SysTime(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12), testTZ)), "timestamptz", "'1997-12-17 07:37:16.000012+02'");

// json
C!PGjson(Json(["float_value": Json(123.456), "text_str": Json("text string")]), "json", `'{"float_value": 123.456,"text_str": "text string"}'`);
Expand All @@ -210,6 +224,7 @@ public void _integration_test( string connParam ) @system
C!TestPath(TestPath(false, [Point(1,1), Point(2,2), Point(3,3)]), "path", "'[(1,1),(2,2),(3,3)]'");
C!Polygon(([Point(1,1), Point(2,2), Point(3,3)]), "polygon", "'((1,1),(2,2),(3,3))'");
C!TestCircle(TestCircle(Point(1,2), 10), "circle", "'<(1,2),10>'");
C!(Nullable!Point)(Nullable!Point(Point(1,2)), "point", "'(1,2)'");

//Arrays
C!(int[][])([[1,2],[3,4]], "int[]", "'{{1,2},{3,4}}'");
Expand Down
8 changes: 8 additions & 0 deletions src/dpq2/oids.d
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ private OidType detectOidTypeNotCareAboutNullable(T)()
import std.datetime.systime : SysTime;
import std.traits : Unqual, isSomeString;
import std.uuid : StdUUID = UUID;
static import dpq2.conv.geometric;
static import dpq2.conv.time;
import vibe.data.json : VibeJson = Json;

Expand All @@ -196,6 +197,13 @@ private OidType detectOidTypeNotCareAboutNullable(T)()
static if(is(UT == VibeJson)){ return Json; } else
static if(is(UT == StdUUID)){ return UUID; } else
static if(is(UT == BitArray)){ return VariableBitString; } else
static if(dpq2.conv.geometric.isValidPointType!UT){ return Point; } else
static if(dpq2.conv.geometric.isValidLineType!UT){ return Line; } else
static if(dpq2.conv.geometric.isValidPathType!UT){ return Path; } else
static if(dpq2.conv.geometric.isValidPolygon!UT){ return Polygon; } else
static if(dpq2.conv.geometric.isValidCircleType!UT){ return Circle; } else
static if(dpq2.conv.geometric.isValidLineSegmentType!UT){ return LineSegment; } else
static if(dpq2.conv.geometric.isValidBoxType!UT){ return Box; } else

static assert(false, "Unsupported D type: "~T.stringof);
}
Expand Down

0 comments on commit c3d2824

Please sign in to comment.