From 15fe95f5327aad4801ddff3d79483af28cca0ea1 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Mon, 2 Jan 2023 16:14:47 +0200 Subject: [PATCH] Refactored various components into their own modules --- source/libpb/deserialization.d | 131 +++++++++++++ source/{libpb.d => libpb/driver.d} | 304 +---------------------------- source/libpb/exceptions.d | 59 ++++++ source/libpb/package.d | 4 + source/libpb/serialization.d | 116 +++++++++++ 5 files changed, 316 insertions(+), 298 deletions(-) create mode 100644 source/libpb/deserialization.d rename source/{libpb.d => libpb/driver.d} (66%) create mode 100644 source/libpb/exceptions.d create mode 100644 source/libpb/package.d create mode 100644 source/libpb/serialization.d diff --git a/source/libpb/deserialization.d b/source/libpb/deserialization.d new file mode 100644 index 0000000..7a84399 --- /dev/null +++ b/source/libpb/deserialization.d @@ -0,0 +1,131 @@ +module libpb.deserialization; + +import std.json; + +public RecordType fromJSON(RecordType)(JSONValue jsonIn) +{ + RecordType record; + + import std.traits; + import std.meta : AliasSeq; + + // Alias as to only expand later when used in compile-time + alias structTypes = FieldTypeTuple!(RecordType); + alias structNames = FieldNameTuple!(RecordType); + alias structValues = record.tupleof; + + static foreach(cnt; 0..structTypes.length) + { + debug(dbg) + { + pragma(msg, structTypes[cnt]); + pragma(msg, structNames[cnt]); + // pragma(msg, structValues[cnt]); + } + + //TODO: Add all integral types + static if(__traits(isSame, mixin(structTypes[cnt]), int)) + { + mixin("record."~structNames[cnt]) = cast(int)jsonIn[structNames[cnt]].integer(); + } + else static if(__traits(isSame, mixin(structTypes[cnt]), uint)) + { + mixin("record."~structNames[cnt]) = cast(uint)jsonIn[structNames[cnt]].integer(); + } + else static if(__traits(isSame, mixin(structTypes[cnt]), ulong)) + { + mixin("record."~structNames[cnt]) = cast(ulong)jsonIn[structNames[cnt]].integer(); + } + else static if(__traits(isSame, mixin(structTypes[cnt]), long)) + { + mixin("record."~structNames[cnt]) = cast(long)jsonIn[structNames[cnt]].integer(); + } + else static if(__traits(isSame, mixin(structTypes[cnt]), string)) + { + mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].str(); + + debug(dbg) + { + pragma(msg,"record."~structNames[cnt]); + } + } + else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue)) + { + mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]]; + + debug(dbg) + { + pragma(msg,"record."~structNames[cnt]); + } + } + else static if(__traits(isSame, mixin(structTypes[cnt]), bool)) + { + mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean(); + + debug(dbg) + { + pragma(msg,"record."~structNames[cnt]); + } + } + //FIXME: Not sure how to get array support going, very new to meta programming + else static if(__traits(isSame, mixin(structTypes[cnt]), mixin(structTypes[cnt])[])) + { + mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean(); + + debug(dbg) + { + pragma(msg,"record."~structNames[cnt]); + } + } + else + { + // throw new + //TODO: Throw error + debug(dbg) + { + pragma(msg, "Unknown type for de-serialization"); + } + } + } + + return record; +} + +unittest +{ + import std.string : cmp; + import std.stdio : writeln; + + struct Person + { + public string firstname, lastname; + public int age; + public bool isMale; + public JSONValue obj; + public int[] list; + } + + JSONValue json = parseJSON(`{ +"firstname" : "Tristan", +"lastname": "Kildaire", +"age": 23, +"obj" : {"bruh":1}, +"isMale": true, +"list": [1,2,3] +} +`); + + Person person = fromJSON!(Person)(json); + + debug(dbg) + { + writeln(person); + } + + assert(cmp(person.firstname, "Tristan") == 0); + assert(cmp(person.lastname, "Kildaire") == 0); + assert(person.age == 23); + assert(person.isMale == true); + assert(person.obj["bruh"].integer() == 1); + //TODO: list test case +} diff --git a/source/libpb.d b/source/libpb/driver.d similarity index 66% rename from source/libpb.d rename to source/libpb/driver.d index e22c9c9..71dbe90 100644 --- a/source/libpb.d +++ b/source/libpb/driver.d @@ -1,69 +1,16 @@ -module libpb; +module libpb.driver; import std.json; import std.stdio; import std.net.curl; import std.conv : to; import std.string : cmp; - -public class PBException : Exception -{ - this() - { - super("bruh todo"); - } -} - -public final class RecordNotFoundException : PBException -{ - public const string offendingTable; - public const string offendingId; - this(string table, string id) - { - this.offendingTable = table; - this.offendingId = id; - } -} - -public final class NotAuthorized : PBException -{ - public const string offendingTable; - public const string offendingId; - this(string table, string id) - { - this.offendingTable = table; - this.offendingId = id; - } -} - -public final class ValidationRequired : PBException -{ - public const string offendingTable; - public const string offendingId; - this(string table, string id) - { - this.offendingTable = table; - this.offendingId = id; - } -} +import libpb.exceptions; +import libpb.serialization; +import libpb.deserialization; - -public final class NetworkException : PBException -{ - this() - { - - } -} - -public final class PocketBaseParsingException : PBException -{ - -} - - -mixin template AuthTokenHeader(alias http, PocketBase pbInstance) +private mixin template AuthTokenHeader(alias http, PocketBase pbInstance) { // Must be an instance of HTTP from `std.curl` static assert(__traits(isSame, typeof(http), HTTP)); @@ -143,7 +90,7 @@ public class PocketBase { writeln("Invalid return from curl_easy_escape"); } - throw new PBException(); + throw new NetworkException(); } // Convert back to D-string (the filter) @@ -461,245 +408,6 @@ public class PocketBase { } - - public static JSONValue serializeRecord(RecordType)(RecordType record) - { - import std.traits; - import std.meta : AliasSeq; - - // Final JSON to submit - JSONValue builtJSON; - - - // Alias as to only expand later when used in compile-time - alias structTypes = FieldTypeTuple!(RecordType); - alias structNames = FieldNameTuple!(RecordType); - alias structValues = record.tupleof; - - static foreach(cnt; 0..structTypes.length) - { - debug(dbg) - { - pragma(msg, structTypes[cnt]); - pragma(msg, structNames[cnt]); - // pragma(msg, structValues[cnt]); - } - - - static if(__traits(isSame, mixin(structTypes[cnt]), int)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), uint)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), ulong)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), long)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), string)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else static if(__traits(isSame, mixin(structTypes[cnt]), bool)) - { - builtJSON[structNames[cnt]] = structValues[cnt]; - } - else - { - debug(dbg) - { - pragma(msg, "Yaa"); - } - builtJSON[structNames[cnt]] = to!(string)(structValues[cnt]); - } - } - - - return builtJSON; - } - - public static fromJSON(RecordType)(JSONValue jsonIn) - { - RecordType record; - - import std.traits; - import std.meta : AliasSeq; - - // Alias as to only expand later when used in compile-time - alias structTypes = FieldTypeTuple!(RecordType); - alias structNames = FieldNameTuple!(RecordType); - alias structValues = record.tupleof; - - static foreach(cnt; 0..structTypes.length) - { - debug(dbg) - { - pragma(msg, structTypes[cnt]); - pragma(msg, structNames[cnt]); - // pragma(msg, structValues[cnt]); - } - - //TODO: Add all integral types - static if(__traits(isSame, mixin(structTypes[cnt]), int)) - { - mixin("record."~structNames[cnt]) = cast(int)jsonIn[structNames[cnt]].integer(); - } - else static if(__traits(isSame, mixin(structTypes[cnt]), uint)) - { - mixin("record."~structNames[cnt]) = cast(uint)jsonIn[structNames[cnt]].integer(); - } - else static if(__traits(isSame, mixin(structTypes[cnt]), ulong)) - { - mixin("record."~structNames[cnt]) = cast(ulong)jsonIn[structNames[cnt]].integer(); - } - else static if(__traits(isSame, mixin(structTypes[cnt]), long)) - { - mixin("record."~structNames[cnt]) = cast(long)jsonIn[structNames[cnt]].integer(); - } - else static if(__traits(isSame, mixin(structTypes[cnt]), string)) - { - mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].str(); - - debug(dbg) - { - pragma(msg,"record."~structNames[cnt]); - } - } - else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue)) - { - mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]]; - - debug(dbg) - { - pragma(msg,"record."~structNames[cnt]); - } - } - else static if(__traits(isSame, mixin(structTypes[cnt]), bool)) - { - mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean(); - - debug(dbg) - { - pragma(msg,"record."~structNames[cnt]); - } - } - //FIXME: Not sure how to get array support going, very new to meta programming - else static if(__traits(isSame, mixin(structTypes[cnt]), mixin(structTypes[cnt])[])) - { - mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean(); - - debug(dbg) - { - pragma(msg,"record."~structNames[cnt]); - } - } - else - { - // throw new - //TODO: Throw error - debug(dbg) - { - pragma(msg, "Unknown type for de-serialization"); - } - } - } - - return record; - } - -} - -public enum EnumType -{ - DOG, - CAT -} - -// Test serialization of a struct to JSON -unittest -{ - import std.algorithm.searching : canFind; - import std.string : cmp; - - - - struct Person - { - public string firstname, lastname; - public int age; - public string[] list; - public JSONValue extraJSON; - public EnumType eType; - } - - Person p1; - p1.firstname = "Tristan"; - p1.lastname = "Kildaire"; - p1.age = 23; - p1.list = ["1", "2", "3"]; - p1.extraJSON = parseJSON(`{"item":1, "items":[1,2,3]}`); - p1.eType = EnumType.CAT; - - JSONValue serialized = PocketBase.serializeRecord(p1); - - string[] keys = serialized.object().keys(); - assert(canFind(keys, "firstname") && cmp(serialized["firstname"].str(), "Tristan") == 0); - assert(canFind(keys, "lastname") && cmp(serialized["lastname"].str(), "Kildaire") == 0); - assert(canFind(keys, "age") && serialized["age"].integer() == 23); - - debug(dbg) - { - writeln(serialized.toPrettyString()); - } -} - - -unittest -{ - import std.string : cmp; - - struct Person - { - public string firstname, lastname; - public int age; - public bool isMale; - public JSONValue obj; - public int[] list; - } - - JSONValue json = parseJSON(`{ -"firstname" : "Tristan", -"lastname": "Kildaire", -"age": 23, -"obj" : {"bruh":1}, -"isMale": true, -"list": [1,2,3] -} -`); - - Person person = PocketBase.fromJSON!(Person)(json); - - debug(dbg) - { - writeln(person); - } - - assert(cmp(person.firstname, "Tristan") == 0); - assert(cmp(person.lastname, "Kildaire") == 0); - assert(person.age == 23); - assert(person.isMale == true); - assert(person.obj["bruh"].integer() == 1); - //TODO: list test case } unittest diff --git a/source/libpb/exceptions.d b/source/libpb/exceptions.d new file mode 100644 index 0000000..e57c473 --- /dev/null +++ b/source/libpb/exceptions.d @@ -0,0 +1,59 @@ +module libpb.exceptions; + +public abstract class PBException : Exception +{ + this(string message = "") + { + super("PBException: "~message); + } +} + +public final class RecordNotFoundException : PBException +{ + public const string offendingTable; + public const string offendingId; + this(string table, string id) + { + this.offendingTable = table; + this.offendingId = id; + + super("Could not find record '"~id~"' in table '"~offendingTable~"'"); + } +} + +public final class NotAuthorized : PBException +{ + public const string offendingTable; + public const string offendingId; + this(string table, string id) + { + this.offendingTable = table; + this.offendingId = id; + } +} + +public final class ValidationRequired : PBException +{ + public const string offendingTable; + public const string offendingId; + this(string table, string id) + { + this.offendingTable = table; + this.offendingId = id; + } +} + + + +public final class NetworkException : PBException +{ + this() + { + + } +} + +public final class PocketBaseParsingException : PBException +{ + +} diff --git a/source/libpb/package.d b/source/libpb/package.d new file mode 100644 index 0000000..c6e14c9 --- /dev/null +++ b/source/libpb/package.d @@ -0,0 +1,4 @@ +module libpb; + +public import libpb.exceptions; +public import libpb.driver; diff --git a/source/libpb/serialization.d b/source/libpb/serialization.d new file mode 100644 index 0000000..f3fd264 --- /dev/null +++ b/source/libpb/serialization.d @@ -0,0 +1,116 @@ +module libpb.serialization; + +import std.json; +import std.conv : to; + +debug(dbg) +{ + import std.stdio : writeln; +} + +public JSONValue serializeRecord(RecordType)(RecordType record) +{ + import std.traits; + import std.meta : AliasSeq; + + // Final JSON to submit + JSONValue builtJSON; + + + // Alias as to only expand later when used in compile-time + alias structTypes = FieldTypeTuple!(RecordType); + alias structNames = FieldNameTuple!(RecordType); + alias structValues = record.tupleof; + + static foreach(cnt; 0..structTypes.length) + { + debug(dbg) + { + pragma(msg, structTypes[cnt]); + pragma(msg, structNames[cnt]); + // pragma(msg, structValues[cnt]); + } + + + static if(__traits(isSame, mixin(structTypes[cnt]), int)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), uint)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), ulong)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), long)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), string)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else static if(__traits(isSame, mixin(structTypes[cnt]), bool)) + { + builtJSON[structNames[cnt]] = structValues[cnt]; + } + else + { + debug(dbg) + { + pragma(msg, "Yaa"); + } + builtJSON[structNames[cnt]] = to!(string)(structValues[cnt]); + } + } + + + return builtJSON; +} + +// Test serialization of a struct to JSON +public enum EnumType +{ + DOG, + CAT +} +unittest +{ + import std.algorithm.searching : canFind; + import std.string : cmp; + + struct Person + { + public string firstname, lastname; + public int age; + public string[] list; + public JSONValue extraJSON; + public EnumType eType; + } + + Person p1; + p1.firstname = "Tristan"; + p1.lastname = "Kildaire"; + p1.age = 23; + p1.list = ["1", "2", "3"]; + p1.extraJSON = parseJSON(`{"item":1, "items":[1,2,3]}`); + p1.eType = EnumType.CAT; + + JSONValue serialized = serializeRecord(p1); + + string[] keys = serialized.object().keys(); + assert(canFind(keys, "firstname") && cmp(serialized["firstname"].str(), "Tristan") == 0); + assert(canFind(keys, "lastname") && cmp(serialized["lastname"].str(), "Kildaire") == 0); + assert(canFind(keys, "age") && serialized["age"].integer() == 23); + + debug(dbg) + { + writeln(serialized.toPrettyString()); + } +}