diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 1a5f6b3..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,18 +0,0 @@
-.dub
-docs.json
-__dummy.html
-docs/
-/libpb
-libpb.so
-libpb.dylib
-libpb.dll
-libpb.a
-libpb.lib
-libpb-test-*
-*.exe
-*.pdb
-*.o
-*.obj
-*.lst
-liblibpb.a
-dub.selections.json
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 0a04128..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/README.md b/README.md
deleted file mode 100644
index 86f78cf..0000000
--- a/README.md
+++ /dev/null
@@ -1,197 +0,0 @@
-![](branding/logo.png)
-
-libpb
-=====
-
-#### _PocketBase wrapper with serializer/deserializer support_
-
-----
-
-## Example usage
-
-View the full API documentation (methods etc.) [here](https://libpb.dpldocs.info/libpb.html).
-
-### Server initiation
-
-Firstly we create a new PocketBase instance to manage our server:
-
-```d
-PocketBase pb = new PocketBase("http://127.0.0.1:8090/api/");
-```
-
-### Serialization
-
-This is just to show off the serialization method `serializeRecord(RecordType)` which returns a `JSONValue` struct:
-
-```d
-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());
-}
-```
-
-### Deserialization
-
-This is to show off deserialization method `fromJSON(RecordType)(JSONValue jsonIn)` which returns a struct of type `RecordType` (so far most features are implemented):
-
-```d
-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 = 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
-```
-
-### Record management
-
-#### Normal collections
-
-Below we have a few calls like create and delete:
-
-```d
-PocketBase pb = new PocketBase();
-
-struct Person
-{
- string id;
- string name;
- int age;
-}
-
-Person p1 = Person();
-p1.name = "Tristan Gonzales";
-p1.age = 23;
-
-Person recordStored = pb.createRecord("dummy", p1);
-pb.deleteRecord("dummy", recordStored.id);
-
-
-recordStored = pb.createRecord("dummy", p1);
-recordStored.age = 46;
-recordStored = pb.updateRecord("dummy", recordStored);
-
-Person recordFetched = pb.viewRecord!(Person)("dummy", recordStored.id);
-
-pb.deleteRecord("dummy", recordStored);
-
-Person[] people = [Person(), Person()];
-people[0].name = "Abby";
-people[1].name = "Becky";
-
-people[0] = pb.createRecord("dummy", people[0]);
-people[1] = pb.createRecord("dummy", people[1]);
-
-Person[] returnedPeople = pb.listRecords!(Person)("dummy");
-foreach(Person returnedPerson; returnedPeople)
-{
- writeln(returnedPerson);
- pb.deleteRecord("dummy", returnedPerson);
-}
-```
-
-#### `auth` collections
-
-Auth collections require that certain calls, such as `createRecord(table, record, isAuthCollection)` have the last argument se to `true`.
-
-```d
-import core.thread : Thread, dur;
-import std.string : cmp;
-
-PocketBase pb = new PocketBase();
-
-struct Person
-{
- string id;
- string email;
- string username;
- string password;
- string passwordConfirm;
-}
-
-Person p1;
-p1.email = "deavmi@redxen.eu";
-p1.username = "deavmi";
-p1.password = "bigbruh1111";
-p1.passwordConfirm = "bigbruh1111";
-
-p1 = pb.createRecordAuth("dummy_auth", p1);
-pb.deleteRecord("dummy_auth", p1);
-```
-
-## Development
-
-### Dependencies
-
-This requires that you have the `libcurl` libraries available for
-linking against.
-
-### Unit tests
-
-To run tests you will want to enable the `pragma`s and `writeln`s. therefore pass the `dbg` flag in as such:
-
-```bash
-dub test -ddbg
-```
-
-Run pocketbase on the default port and then use the schema provided as `dummy.json` to test with (in a collection named `dummy`).
-
-## License
-
-See [LICENSE](LICENSE)
diff --git a/branding/logo.xcf b/branding/logo.xcf
deleted file mode 100644
index 597795a..0000000
Binary files a/branding/logo.xcf and /dev/null differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..1e1b488
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,29 @@
+libpb
+=====
+
+#### _PocketBase wrapper with serializer/deserializer support_
+
+----
+
+## API
+
+For full API documentation see [DUB API Spec](https://libpb.dpldocs.info/index.html).
+
+## Installing
+
+In order to use libpb in your project simply run:
+
+```bash
+dub add libpb
+```
+
+And then in your D program import as follows:
+
+```d
+import libpb;
+```
+
+### Dependencies
+
+This requires that you have the `libcurl` libraries available for
+linking against.
\ No newline at end of file
diff --git a/branding/logo.png b/docs/logo.png
similarity index 100%
rename from branding/logo.png
rename to docs/logo.png
diff --git a/docs/record management/auth collections.md b/docs/record management/auth collections.md
new file mode 100644
index 0000000..3b59fe4
--- /dev/null
+++ b/docs/record management/auth collections.md
@@ -0,0 +1,29 @@
+`auth` collections
+==================
+
+Auth collections require that certain calls, such as `createRecord(table, record, isAuthCollection)` have the last argument se to `true`.
+
+```d
+import core.thread : Thread, dur;
+import std.string : cmp;
+
+PocketBase pb = new PocketBase();
+
+struct Person
+{
+ string id;
+ string email;
+ string username;
+ string password;
+ string passwordConfirm;
+}
+
+Person p1;
+p1.email = "deavmi@redxen.eu";
+p1.username = "deavmi";
+p1.password = "bigbruh1111";
+p1.passwordConfirm = "bigbruh1111";
+
+p1 = pb.createRecordAuth("dummy_auth", p1);
+pb.deleteRecord("dummy_auth", p1);
+```
\ No newline at end of file
diff --git a/docs/record management/base collections.md b/docs/record management/base collections.md
new file mode 100644
index 0000000..663403b
--- /dev/null
+++ b/docs/record management/base collections.md
@@ -0,0 +1,45 @@
+Base collections
+================
+
+Below we have a few calls like create and delete:
+
+```d
+PocketBase pb = new PocketBase();
+
+struct Person
+{
+ string id;
+ string name;
+ int age;
+}
+
+Person p1 = Person();
+p1.name = "Tristan Gonzales";
+p1.age = 23;
+
+Person recordStored = pb.createRecord("dummy", p1);
+pb.deleteRecord("dummy", recordStored.id);
+
+
+recordStored = pb.createRecord("dummy", p1);
+recordStored.age = 46;
+recordStored = pb.updateRecord("dummy", recordStored);
+
+Person recordFetched = pb.viewRecord!(Person)("dummy", recordStored.id);
+
+pb.deleteRecord("dummy", recordStored);
+
+Person[] people = [Person(), Person()];
+people[0].name = "Abby";
+people[1].name = "Becky";
+
+people[0] = pb.createRecord("dummy", people[0]);
+people[1] = pb.createRecord("dummy", people[1]);
+
+Person[] returnedPeople = pb.listRecords!(Person)("dummy");
+foreach(Person returnedPerson; returnedPeople)
+{
+ writeln(returnedPerson);
+ pb.deleteRecord("dummy", returnedPerson);
+}
+```
\ No newline at end of file
diff --git a/docs/server initialization.md b/docs/server initialization.md
new file mode 100644
index 0000000..5d845b4
--- /dev/null
+++ b/docs/server initialization.md
@@ -0,0 +1,8 @@
+Server initiation
+=================
+
+Firstly we create a new PocketBase instance to manage our server:
+
+```d
+PocketBase pb = new PocketBase("http://127.0.0.1:8090/api/");
+```
\ No newline at end of file
diff --git a/docs/translation/deserialization.md b/docs/translation/deserialization.md
new file mode 100644
index 0000000..6fe8b8d
--- /dev/null
+++ b/docs/translation/deserialization.md
@@ -0,0 +1,42 @@
+Deserialization
+===============
+
+This is to show off deserialization method `fromJSON(RecordType)(JSONValue jsonIn)` which returns a struct of type `RecordType` (so far most features are implemented):
+
+```d
+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 = 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
+```
\ No newline at end of file
diff --git a/docs/translation/serialization.md b/docs/translation/serialization.md
new file mode 100644
index 0000000..c79d6e4
--- /dev/null
+++ b/docs/translation/serialization.md
@@ -0,0 +1,38 @@
+Serialization
+=============
+
+This is just to show off the serialization method `serializeRecord(RecordType)` which returns a `JSONValue` struct:
+
+```d
+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());
+}
+```
\ No newline at end of file
diff --git a/dub.json b/dub.json
deleted file mode 100644
index e55e8ef..0000000
--- a/dub.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "authors": [
- "Tristan B. Velloza Kildaire"
- ],
- "copyright": "Copyright © 2022, Tristan B. Velloza Kildaire",
- "dependencies": {
- "jstruct": "0.1.2"
- },
- "description": "PocketBase wrapper with serializer/deserializer support",
- "libs": [
- "curl"
- ],
- "license": "LGPL v3.0",
- "name": "libpb",
- "targetType": "library"
-}
diff --git a/dummy.json b/dummy.json
deleted file mode 100644
index adbc2ca..0000000
--- a/dummy.json
+++ /dev/null
@@ -1,89 +0,0 @@
-[
- {
- "id": "iir9gleipa4n6lf",
- "name": "dummy",
- "type": "base",
- "system": false,
- "schema": [
- {
- "id": "fkyktfrf",
- "name": "name",
- "type": "text",
- "system": false,
- "required": false,
- "unique": false,
- "options": {
- "min": null,
- "max": null,
- "pattern": ""
- }
- },
- {
- "id": "31mcl2nq",
- "name": "age",
- "type": "number",
- "system": false,
- "required": false,
- "unique": false,
- "options": {
- "min": null,
- "max": null
- }
- }
- ],
- "listRule": "",
- "viewRule": "",
- "createRule": "",
- "updateRule": "",
- "deleteRule": "",
- "options": {}
- },
- {
- "id": "fw99z50dwcfjwn7",
- "name": "dummy_auth",
- "type": "auth",
- "system": false,
- "schema": [
- {
- "id": "psy7unkl",
- "name": "name",
- "type": "text",
- "system": false,
- "required": false,
- "unique": false,
- "options": {
- "min": null,
- "max": null,
- "pattern": ""
- }
- },
- {
- "id": "jroziygp",
- "name": "age",
- "type": "number",
- "system": false,
- "required": false,
- "unique": false,
- "options": {
- "min": null,
- "max": null
- }
- }
- ],
- "listRule": "",
- "viewRule": "",
- "createRule": "",
- "updateRule": "",
- "deleteRule": "",
- "options": {
- "allowEmailAuth": true,
- "allowOAuth2Auth": false,
- "allowUsernameAuth": true,
- "exceptEmailDomains": null,
- "manageRule": null,
- "minPasswordLength": 8,
- "onlyEmailDomains": null,
- "requireEmail": false
- }
- }
-]
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..57230a0
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,5 @@
+site_name: libpb
+theme: alabaster
+
+extra:
+ logo: logo.png
\ No newline at end of file
diff --git a/source/libpb/driver.d b/source/libpb/driver.d
deleted file mode 100644
index f470fb6..0000000
--- a/source/libpb/driver.d
+++ /dev/null
@@ -1,923 +0,0 @@
-module libpb.driver;
-
-import std.json;
-import std.stdio;
-import std.net.curl;
-import std.conv : to;
-import std.string : cmp;
-import libpb.exceptions;
-import jstruct : fromJSON, SerializationError, serializeRecord;
-
-
-private mixin template AuthTokenHeader(alias http, PocketBase pbInstance)
-{
- // Must be an instance of HTTP from `std.curl`
- static assert(__traits(isSame, typeof(http), HTTP));
-
- void InitializeAuthHeader()
- {
- // Check if the given PocketBase instance as an authToken
- if(pbInstance.authToken.length > 0)
- {
- // Then add the authaorization header
- http.addRequestHeader("Authorization", pbInstance.getAuthToken());
- }
- }
-
-}
-
-public class PocketBase
-{
- private string pocketBaseURL;
- private string authToken;
-
- /**
- * Constructs a new PocketBase instance with
- * the default settings
- */
- this(string pocketBaseURL = "http://127.0.0.1:8090/api/", string authToken = "")
- {
- this.pocketBaseURL = pocketBaseURL;
- this.authToken = authToken;
- }
-
- public void setAuthToken(string authToken)
- {
- if(cmp(authToken, "") != 0)
- {
- this.authToken = authToken;
- }
- }
-
- public string getAuthToken()
- {
- return this.authToken;
- }
-
- /**
- * List all of the records in the given table (base collection)
- *
- * Params:
- * table = the table to list from
- * page = the page to look at (default is 1)
- * perPage = the number of items to return per page (default is 30)
- * filter = the predicate to filter by
- *
- * Returns: A list of type RecordType
- */
- public RecordType[] listRecords(RecordType)(string table, ulong page = 1, ulong perPage = 30, string filter = "")
- {
- return listRecords_internal!(RecordType)(table, page, perPage, filter, false);
- }
-
- /**
- * List all of the records in the given table (auth collection)
- *
- * Params:
- * table = the table to list from
- * page = the page to look at (default is 1)
- * perPage = the number of items to return per page (default is 30)
- * filter = the predicate to filter by
- *
- * Returns: A list of type RecordType
- */
- public RecordType[] listRecordsAuth(RecordType)(string table, ulong page = 1, ulong perPage = 30, string filter = "")
- {
- return listRecords_internal!(RecordType)(table, page, perPage, filter, true);
- }
-
- /**
- * List all of the records in the given table (internals)
- *
- * Params:
- * table = the table to list from
- * page = the page to look at (default is 1)
- * perPage = the number of items to return per page (default is 30)
- * filter = the predicate to filter by
- * isAuthCollection = true if this is an auth collection, false
- * for base collection
- *
- * Returns: A list of type RecordType
- */
- private RecordType[] listRecords_internal(RecordType)(string table, ulong page, ulong perPage, string filter, bool isAuthCollection)
- {
- // Set authorization token if setup
- HTTP httpSettings = HTTP();
- mixin AuthTokenHeader!(httpSettings, this);
- InitializeAuthHeader();
-
- RecordType[] recordsOut;
-
- // Compute the query string
- string queryStr = "page="~to!(string)(page)~"&perPage="~to!(string)(perPage);
-
- // If there is a filter then perform the needed escaping
- if(cmp(filter, "") != 0)
- {
- // For the filter, make sure to add URL escaping to the `filter` parameter
- import etc.c.curl : curl_escape;
- import std.string : toStringz, fromStringz;
- char* escapedParameter = curl_escape(toStringz(filter), cast(int)filter.length);
- if(escapedParameter is null)
- {
- debug(dbg)
- {
- writeln("Invalid return from curl_easy_escape");
- }
- throw new NetworkException();
- }
-
- // Convert back to D-string (the filter)
- filter = cast(string)fromStringz(escapedParameter);
- }
-
- // Append the filter
- queryStr ~= cmp(filter, "") == 0 ? "" : "&filter="~filter;
-
- try
- {
- string responseData = cast(string)get(pocketBaseURL~"collections/"~table~"/records?"~queryStr, httpSettings);
- JSONValue responseJSON = parseJSON(responseData);
- JSONValue[] returnedItems = responseJSON["items"].array();
-
- foreach(JSONValue returnedItem; returnedItems)
- {
- // If this is an authable record (meaning it has email, password and passwordConfirm)
- // well then the latter two will not be returned so fill them in. Secondly, the email
- // will only be returned if `emailVisibility` is true.
- if(isAuthCollection)
- {
- returnedItem["password"] = "";
- returnedItem["passwordConfirm"] = "";
-
- // If email is invisible make a fake field to prevent crash
- if(!returnedItem["emailVisibility"].boolean())
- {
- returnedItem["email"] = "";
- }
- }
-
- recordsOut ~= fromJSON!(RecordType)(returnedItem);
- }
-
- return recordsOut;
- }
- catch(HTTPStatusException e)
- {
- if(e.status == 403)
- {
- throw new NotAuthorized(table, null);
- }
- else
- {
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- debug(dbg)
- {
- writeln("curl");
- writeln(e);
- }
-
- throw new NetworkException();
- }
- catch(JSONException e)
- {
- throw new PocketBaseParsingException();
- }
- catch(SerializationError e)
- {
- throw new RemoteFieldMissing();
- }
- }
-
- /**
- * Creates a record in the given authentication table
- *
- * Params:
- * table = the table to create the record in
- * item = The Record to create
- *
- * Returns: An instance of the created RecordType
- */
- public RecordType createRecordAuth(string, RecordType)(string table, RecordType item)
- {
- mixin isAuthable!(RecordType);
-
- return createRecord_internal(table, item, true);
- }
-
- /**
- * Creates a record in the given base table
- *
- * Params:
- * table = the table to create the record in
- * item = The Record to create
- *
- * Returns: An instance of the created RecordType
- */
- public RecordType createRecord(string, RecordType)(string table, RecordType item)
- {
- return createRecord_internal(table, item, false);
- }
-
- /**
- * Creates a record in the given table (internal method)
- *
- * Params:
- * table = the table to create the record in
- * item = The Record to create
- * isAuthCollection = whether or not this collection is auth or not (base)
- *
- * Returns: An instance of the created RecordType
- */
- private RecordType createRecord_internal(string, RecordType)(string table, RecordType item, bool isAuthCollection)
- {
- idAbleCheck(item);
-
- RecordType recordOut;
-
- // Set authorization token if setup
- HTTP httpSettings = HTTP();
- mixin AuthTokenHeader!(httpSettings, this);
- InitializeAuthHeader();
-
- // Set the content type
- httpSettings.addRequestHeader("Content-Type", "application/json");
-
- // Serialize the record instance
- JSONValue serialized = serializeRecord(item);
-
- try
- {
- string responseData = cast(string)post(pocketBaseURL~"collections/"~table~"/records", serialized.toString(), httpSettings);
- JSONValue responseJSON = parseJSON(responseData);
-
- // On creation of a record in an "auth" collection the email visibility
- // will initially be false, therefore fill in a blank for it temporarily
- // now as to not make `fromJSON` crash when it sees an email field in
- // a struct and tries to look the the JSON key "email" when it isn't present
- //
- // A password is never returned (so `password` and `passwordConfirm` will be left out)
- //
- // The above are all assumed to be strings, if not then a runtime error will occur
- // See (issue #3)
- if(isAuthCollection)
- {
- responseJSON["email"] = "";
- responseJSON["password"] = "";
- responseJSON["passwordConfirm"] = "";
- }
-
- recordOut = fromJSON!(RecordType)(responseJSON);
-
- return recordOut;
- }
- catch(HTTPStatusException e)
- {
- debug(dbg)
- {
- writeln("createRecord_internal: "~e.toString());
- }
-
- if(e.status == 403)
- {
- throw new NotAuthorized(table, item.id);
- }
- else if(e.status == 400)
- {
- throw new ValidationRequired(table, item.id);
- }
- else
- {
- // TODO: Fix this
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- throw new NetworkException();
- }
- catch(JSONException e)
- {
- throw new PocketBaseParsingException();
- }
- catch(SerializationError e)
- {
- throw new RemoteFieldMissing();
- }
- }
-
- /**
- * Authenticates on the given auth table with the provided
- * credentials, returning a JWT token in the reference parameter.
- * Finally returning the record of the authenticated user.
- *
- * Params:
- * table = the auth collection to use
- * identity = the user's identity
- * password = the user's password
- * token = the variable to return into
- *
- * Returns: An instance of `RecordType`
- */
- public RecordType authWithPassword(RecordType)(string table, string identity, string password, ref string token)
- {
- mixin isAuthable!(RecordType);
-
- RecordType recordOut;
-
- // Set the content type
- HTTP httpSettings = HTTP();
- httpSettings.addRequestHeader("Content-Type", "application/json");
-
- // Construct the authentication record
- JSONValue authRecord;
- authRecord["identity"] = identity;
- authRecord["password"] = password;
-
- try
- {
- string responseData = cast(string)post(pocketBaseURL~"collections/"~table~"/auth-with-password", authRecord.toString(), httpSettings);
- JSONValue responseJSON = parseJSON(responseData);
- JSONValue recordResponse = responseJSON["record"];
-
- // In the case we are doing auth, we won't get password, passwordConfirm sent back
- // set them to empty
- recordResponse["password"] = "";
- recordResponse["passwordConfirm"] = "";
-
- // If email is invisible make a fake field to prevent crash
- if(!recordResponse["emailVisibility"].boolean())
- {
- recordResponse["email"] = "";
- }
-
- recordOut = fromJSON!(RecordType)(recordResponse);
-
- // Store the token
- token = responseJSON["token"].str();
-
- return recordOut;
- }
- catch(HTTPStatusException e)
- {
- if(e.status == 400)
- {
- // TODO: Update this error
- throw new NotAuthorized(table, null);
- }
- else
- {
- // TODO: Fix this
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- throw new NetworkException();
- }
- catch(JSONException e)
- {
- throw new PocketBaseParsingException();
- }
- catch(SerializationError e)
- {
- throw new RemoteFieldMissing();
- }
- }
-
- /**
- * View the given record by id (base collections)
- *
- * Params:
- * table = the table to lookup the record in
- * id = the id to lookup the record by
- *
- * Returns: The found record of type RecordType
- */
- public RecordType viewRecord(RecordType)(string table, string id)
- {
- return viewRecord_internal!(RecordType)(table, id, false);
- }
-
-
- /**
- * View the given record by id (auth collections)
- *
- * Params:
- * table = the table to lookup the record in
- * id = the id to lookup the record by
- *
- * Returns: The found record of type RecordType
- */
- public RecordType viewRecordAuth(RecordType)(string table, string id)
- {
- return viewRecord_internal!(RecordType)(table, id, true);
- }
-
- /**
- * View the given record by id (internal)
- *
- * Params:
- * table = the table to lookup the record in
- * id = the id to lookup the record by
- * isAuthCollection = true if this is an auth collection, false
- * for base collection
- *
- * Returns: The found record of type RecordType
- */
- private RecordType viewRecord_internal(RecordType)(string table, string id, bool isAuthCollection)
- {
- RecordType recordOut;
-
- // Set authorization token if setup
- HTTP httpSettings = HTTP();
- mixin AuthTokenHeader!(httpSettings, this);
- InitializeAuthHeader();
-
- try
- {
- string responseData = cast(string)get(pocketBaseURL~"collections/"~table~"/records/"~id, httpSettings);
- JSONValue responseJSON = parseJSON(responseData);
-
- // If this is an authable record (meaning it has email, password and passwordConfirm)
- // well then the latter two will not be returned so fill them in. Secondly, the email
- // will only be returned if `emailVisibility` is true.
- if(isAuthCollection)
- {
- responseJSON["password"] = "";
- responseJSON["passwordConfirm"] = "";
-
- // If email is invisible make a fake field to prevent crash
- if(!responseJSON["emailVisibility"].boolean())
- {
- responseJSON["email"] = "";
- }
- }
-
- recordOut = fromJSON!(RecordType)(responseJSON);
-
- return recordOut;
- }
- catch(HTTPStatusException e)
- {
- if(e.status == 404)
- {
- throw new RecordNotFoundException(table, id);
- }
- else
- {
- // TODO: Fix this
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- throw new NetworkException();
- }
- catch(JSONException e)
- {
- throw new PocketBaseParsingException();
- }
- catch(SerializationError e)
- {
- throw new RemoteFieldMissing();
- }
- }
-
- /**
- * Updates the given record in the given table, returning the
- * updated record (auth collections)
- *
- * Params:
- * table = tabe table to update the record in
- * item = the record of type RecordType
to update
- *
- * Returns: The updated RecordType
- */
- public RecordType updateRecordAuth(string, RecordType)(string table, RecordType item)
- {
- return updateRecord_internal(table, item, true);
- }
-
- /**
- * Updates the given record in the given table, returning the
- * updated record (base collections)
- *
- * Params:
- * table = tabe table to update the record in
- * item = the record of type RecordType
to update
- *
- * Returns: The updated RecordType
- */
- public RecordType updateRecord(string, RecordType)(string table, RecordType item)
- {
- return updateRecord_internal(table, item, false);
- }
-
- /**
- * Updates the given record in the given table, returning the
- * updated record (internal)
- *
- * Params:
- * table = tabe table to update the record in
- * item = the record of type RecordType
to update
- * isAuthCollection = true if this is an auth collection, false
- * for base collection
- *
- * Returns: The updated RecordType
- */
- private RecordType updateRecord_internal(string, RecordType)(string table, RecordType item, bool isAuthCollection)
- {
- idAbleCheck(item);
-
- RecordType recordOut;
-
- // Set authorization token if setup
- HTTP httpSettings = HTTP();
- mixin AuthTokenHeader!(httpSettings, this);
- InitializeAuthHeader();
-
- // Set the content type
- httpSettings.addRequestHeader("Content-Type", "application/json");
-
- // Serialize the record instance
- JSONValue serialized = serializeRecord(item);
-
- try
- {
- string responseData = cast(string)patch(pocketBaseURL~"collections/"~table~"/records/"~item.id, serialized.toString(), httpSettings);
- JSONValue responseJSON = parseJSON(responseData);
-
- // If this is an authable record (meaning it has email, password and passwordConfirm)
- // well then the latter two will not be returned so fill them in. Secondly, the email
- // will only be returned if `emailVisibility` is true.
- if(isAuthCollection)
- {
- responseJSON["password"] = "";
- responseJSON["passwordConfirm"] = "";
-
- // If email is invisible make a fake field to prevent crash
- if(!responseJSON["emailVisibility"].boolean())
- {
- responseJSON["email"] = "";
- }
- }
-
- recordOut = fromJSON!(RecordType)(responseJSON);
-
- return recordOut;
- }
- catch(HTTPStatusException e)
- {
- if(e.status == 404)
- {
- throw new RecordNotFoundException(table, item.id);
- }
- else if(e.status == 403)
- {
- throw new NotAuthorized(table, item.id);
- }
- else if(e.status == 400)
- {
- throw new ValidationRequired(table, item.id);
- }
- else
- {
- // TODO: Fix this
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- throw new NetworkException();
- }
- catch(JSONException e)
- {
- throw new PocketBaseParsingException();
- }
- catch(SerializationError e)
- {
- throw new RemoteFieldMissing();
- }
- }
-
- /**
- * Deletes the provided record by id from the given table
- *
- * Params:
- * table = the table to delete the record from
- * id = the id of the record to delete
- */
- public void deleteRecord(string table, string id)
- {
- // Set authorization token if setup
- HTTP httpSettings = HTTP();
- mixin AuthTokenHeader!(httpSettings, this);
- InitializeAuthHeader();
-
- try
- {
- del(pocketBaseURL~"collections/"~table~"/records/"~id, httpSettings);
- }
- catch(HTTPStatusException e)
- {
- if(e.status == 404)
- {
- throw new RecordNotFoundException(table, id);
- }
- else
- {
- // TODO: Fix this
- throw new NetworkException();
- }
- }
- catch(CurlException e)
- {
- throw new NetworkException();
- }
- }
-
- /**
- * Deletes the provided record from the given table
- *
- * Params:
- * table = the table to delete from
- * record = the record of type RecordType
to delete
- */
- public void deleteRecord(string, RecordType)(string table, RecordType record)
- {
- idAbleCheck(record);
- deleteRecord(table, record.id);
- }
-
- private mixin template MemberAndType(alias record, alias typeEnforce, string memberName)
- {
- static if(__traits(hasMember, record, memberName))
- {
- static if(__traits(isSame, typeof(mixin("record."~memberName)), typeEnforce))
- {
-
- }
- else
- {
- pragma(msg, "Member '"~memberName~"' not of type '"~typeEnforce~"'");
- static assert(false);
- }
- }
- else
- {
- pragma(msg, "Record does not have member '"~memberName~"'");
- static assert(false);
- }
- }
-
- private static void isAuthable(RecordType)(RecordType record)
- {
- mixin MemberAndType!(record, string, "email");
- mixin MemberAndType!(record, string, "password");
- mixin MemberAndType!(record, string, "passwordConfirm");
- }
-
- private static void idAbleCheck(RecordType)(RecordType record)
- {
- static if(__traits(hasMember, record, "id"))
- {
- static if(__traits(isSame, typeof(record.id), string))
- {
- // Do nothing as it is a-okay
- }
- else
- {
- // Must be a string
- pragma(msg, "The `id` field of the record provided must be of type string");
- static assert(false);
- }
- }
- else
- {
- // An id field is required (TODO: ensure not a function identifier)
- pragma(msg, "The provided record must have a `id` field");
- static assert(false);
- }
- }
-
- // TODO: Implement the streaming functionality
- private void stream(string table)
- {
-
- }
-}
-
-unittest
-{
- import core.thread : Thread, dur;
- import std.string : cmp;
-
- PocketBase pb = new PocketBase();
-
- struct Person
- {
- string id;
- string name;
- int age;
- }
-
- Person p1 = Person();
- p1.name = "Tristan Gonzales";
- p1.age = 23;
-
- Person recordStored = pb.createRecord("dummy", p1);
- pb.deleteRecord("dummy", recordStored.id);
-
-
- recordStored = pb.createRecord("dummy", p1);
- Thread.sleep(dur!("seconds")(3));
- recordStored.age = 46;
- recordStored = pb.updateRecord("dummy", recordStored);
- assert(recordStored.age == 46);
- Thread.sleep(dur!("seconds")(3));
-
- Person recordFetched = pb.viewRecord!(Person)("dummy", recordStored.id);
- assert(recordFetched.age == 46);
- assert(cmp(recordFetched.name, "Tristan Gonzales") == 0);
- assert(cmp(recordFetched.id, recordStored.id) == 0);
-
- pb.deleteRecord("dummy", recordStored);
-
- Person[] people = [Person(), Person()];
- people[0].name = "Abby";
- people[1].name = "Becky";
-
- people[0] = pb.createRecord("dummy", people[0]);
- people[1] = pb.createRecord("dummy", people[1]);
-
- Person[] returnedPeople = pb.listRecords!(Person)("dummy");
- foreach(Person returnedPerson; returnedPeople)
- {
- debug(dbg)
- {
- writeln(returnedPerson);
- }
- pb.deleteRecord("dummy", returnedPerson);
- }
-
- try
- {
- recordFetched = pb.viewRecord!(Person)("dummy", people[0].id);
- assert(false);
- }
- catch(RecordNotFoundException e)
- {
- assert(cmp(e.offendingTable, "dummy") == 0 && e.offendingId == people[0].id);
- }
- catch(Exception e)
- {
- assert(false);
- }
-
- try
- {
- recordFetched = pb.updateRecord("dummy", people[0]);
- assert(false);
- }
- catch(RecordNotFoundException e)
- {
- assert(cmp(e.offendingTable, "dummy") == 0 && e.offendingId == people[0].id);
- }
- catch(Exception e)
- {
- assert(false);
- }
-
- try
- {
- pb.deleteRecord("dummy", people[0]);
- assert(false);
- }
- catch(RecordNotFoundException e)
- {
- assert(cmp(e.offendingTable, "dummy") == 0 && e.offendingId == people[0].id);
- }
- catch(Exception e)
- {
- assert(false);
- }
-}
-
-unittest
-{
- import core.thread : Thread, dur;
- import std.string : cmp;
-
- PocketBase pb = new PocketBase();
-
- struct Person
- {
- string id;
- string email;
- string username;
- string password;
- string passwordConfirm;
- string name;
- int age;
- }
-
- // Set the password to use
- string passwordToUse = "bigbruh1111";
-
- Person p1;
- p1.email = "deavmi@redxen.eu";
- p1.username = "deavmi";
- p1.password = passwordToUse;
- p1.passwordConfirm = passwordToUse;
- p1.name = "Tristaniha";
- p1.age = 29;
-
- p1 = pb.createRecordAuth("dummy_auth", p1);
-
-
- Person[] people = pb.listRecordsAuth!(Person)("dummy_auth", 1, 30, "(id='"~p1.id~"')");
- assert(people.length == 1);
-
- // Ensure we get our person back
- assert(cmp(people[0].name, p1.name) == 0);
- assert(people[0].age == p1.age);
- // assert(cmp(people[0].email, p1.email) == 0);
-
-
- Person person = pb.viewRecordAuth!(Person)("dummy_auth", p1.id);
-
- // Ensure we get our person back
- assert(cmp(people[0].name, p1.name) == 0);
- assert(people[0].age == p1.age);
- // assert(cmp(people[0].email, p1.email) == 0);
-
-
- string newName = "Bababooey";
- person.name = newName;
- person = pb.updateRecordAuth("dummy_auth", person);
- assert(cmp(person.name, newName) == 0);
-
-
-
- string tokenIn;
- Person authPerson = pb.authWithPassword!(Person)("dummy_auth", p1.username, passwordToUse, tokenIn);
-
- // Ensure a non-empty token
- assert(cmp(tokenIn, "") != 0);
- writeln("Token: "~tokenIn);
-
- // Ensure we get our person back
- assert(cmp(authPerson.name, person.name) == 0);
- assert(authPerson.age == person.age);
- assert(cmp(authPerson.email, person.email) == 0);
-
- // Delete the record
- pb.deleteRecord("dummy_auth", p1);
-}
-
-unittest
-{
- import core.thread : Thread, dur;
- import std.string : cmp;
-
- PocketBase pb = new PocketBase();
-
- struct Person
- {
- string id;
- string name;
- int age;
- }
-
- Person p1 = Person();
- p1.name = "Tristan Gonzales";
- p1.age = 23;
-
- Person p2 = Person();
- p2.name = p1.name~"2";
- p2.age = p1.age;
-
- p1 = pb.createRecord("dummy", p1);
- p2 = pb.createRecord("dummy", p2);
-
- Person[] people = pb.listRecords!(Person)("dummy", 1, 30, "(id='"~p1.id~"')");
- assert(people.length == 1);
- assert(cmp(people[0].id, p1.id) == 0);
-
- pb.deleteRecord("dummy", p1);
- people = pb.listRecords!(Person)("dummy", 1, 30, "(id='"~p1.id~"')");
- assert(people.length == 0);
-
- people = pb.listRecords!(Person)("dummy", 1, 30, "(id='"~p2.id~"' && age=24)");
- assert(people.length == 0);
-
- people = pb.listRecords!(Person)("dummy", 1, 30, "(id='"~p2.id~"' && age=23)");
- assert(people.length == 1 && cmp(people[0].id, p2.id) == 0);
-
- pb.deleteRecord("dummy", p2);
-}
\ No newline at end of file
diff --git a/source/libpb/exceptions.d b/source/libpb/exceptions.d
deleted file mode 100644
index 6744988..0000000
--- a/source/libpb/exceptions.d
+++ /dev/null
@@ -1,72 +0,0 @@
-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;
- }
-}
-
-
-/**
- * NetworkException
- *
- * Thrown on an unhandled curl error
- */
-public final class NetworkException : PBException
-{
- this()
- {
-
- }
-}
-
-public final class PocketBaseParsingException : PBException
-{
-
-}
-
-
-public final class RemoteFieldMissing : PBException
-{
- this()
- {
-
- }
-}
\ No newline at end of file
diff --git a/source/libpb/package.d b/source/libpb/package.d
deleted file mode 100644
index 7bf3fe1..0000000
--- a/source/libpb/package.d
+++ /dev/null
@@ -1,4 +0,0 @@
-module libpb;
-
-public import libpb.exceptions;
-public import libpb.driver;
\ No newline at end of file