katja's git: flauschehorn.sexy

source-code of flauschehorn.sexy

commit 503b9702742c1695aa41b51587cb6258ab2a5fca
parent 58bd2a442c34488fc4411e31ab299d0af1e06600
Author: Katja (ctucx) <git@ctu.cx>
Date: Mon, 17 Mar 2025 12:36:44 +0100

flake: update nixpkgs, fetch deps from git instead of bundling them
12 files changed, 26 insertions(+), 1800 deletions(-)
M
flake.lock
|
8
++++----
M
flake.nix
|
22
++++++++++++++++++----
D
src/libs/moustachu.nim
|
197
-------------------------------------------------------------------------------
D
src/libs/moustachu_context.nim
|
273
-------------------------------------------------------------------------------
D
src/libs/moustachu_tokenizer.nim
|
128
-------------------------------------------------------------------------------
D
src/libs/tiny_sqlite.nim
|
663
-------------------------------------------------------------------------------
D
src/libs/tiny_sqlite/private/documentation.rst
|
182
-------------------------------------------------------------------------------
D
src/libs/tiny_sqlite/private/stmtcache.nim
|
81
-------------------------------------------------------------------------------
D
src/libs/tiny_sqlite/sqlite_wrapper.nim
|
264
-------------------------------------------------------------------------------
M
src/mastofetch.nim
|
2
+-
M
src/website.nim
|
4
++--
M
src/website.tpl
|
2
+-
diff --git a/flake.lock b/flake.lock
@@ -2,16 +2,16 @@
   "nodes": {
     "nixpkgs": {
       "locked": {
-        "lastModified": 1719426051,
-        "narHash": "sha256-yJL9VYQhaRM7xs0M867ZFxwaONB9T2Q4LnGo1WovuR4=",
+        "lastModified": 1742136038,
+        "narHash": "sha256-DDe16FJk18sadknQKKG/9FbwEro7A57tg9vB5kxZ8kY=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "89c49874fb15f4124bf71ca5f42a04f2ee5825fd",
+        "rev": "a1185f4064c18a5db37c5c84e5638c78b46e3341",
         "type": "github"
       },
       "original": {
         "owner": "NixOS",
-        "ref": "nixos-24.05",
+        "ref": "nixos-24.11",
         "repo": "nixpkgs",
         "type": "github"
       }
diff --git a/flake.nix b/flake.nix
@@ -2,7 +2,7 @@
 
   description = "";
 
-  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
+  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
 
   outputs = inputs: let
     forAllSystems = function:

@@ -23,12 +23,26 @@
     });
 
     overlays.default = final: prev: {
-      flauschehorn-sexy = final.buildNimPackage {
+      flauschehorn-sexy = let
+        nim-mustache = final.fetchFromGitHub {
+          owner  = "soasme";
+          repo   = "nim-mustache";
+          rev    = "v0.4.3";
+          sha256 = "sha256-rrmKSb422YALxg0nV8rjTNgLecJAM8jvg8tnbvSa9SY";
+        };
+
+        tiny_sqlite = final.fetchFromGitHub {
+          owner  = "GULPF";
+          repo   = "tiny_sqlite";
+          rev    = "v0.2.0";
+          sha256 = "sha256-Vc4ju/9DUpMO2Ju4XirKRH+8Goa5BLENkhIM2I3/hBQ=";
+        };
+
+      in final.buildNimPackage {
         name = "flauschehorn-sexy";
         src  = inputs.self;
 
-        buildInputs = [ final.sqlite ];
-
+        buildInputs = [ final.openssl final.sqlite nim-mustache tiny_sqlite ];
         nimRelease  = true;
       };
     };
diff --git a/src/libs/moustachu.nim b/src/libs/moustachu.nim
@@ -1,197 +0,0 @@
-## This is basicly the code from fenekku's moustachu project with a few changes
-## you can find it here: https://github.com/fenekku/moustachu
-
-## A mustache templating engine written in Nim.
-import strutils, sequtils, json
-import moustachu_context, moustachu_tokenizer
-
-export moustachu_context
-
-proc lookupContext (contextStack: seq[Context], tagkey: string): Context =
-  ## Return the Context associated with `tagkey` where `tagkey`
-  ## can be a dotted tag e.g. a.b.c
-  ## If the Context at `tagkey` does not exist, return nil.
-  var currCtx = contextStack[contextStack.high]
-  if tagkey == ".": return currCtx
-  let subtagkeys = tagkey.split(".")
-  for i in countDown(contextStack.high, contextStack.low):
-    currCtx = contextStack[i]
-
-    for subtagkey in subtagkeys:
-      currCtx = currCtx[subtagkey]
-      if currCtx == nil:
-        break
-
-    if currCtx != nil:
-      return currCtx
-
-  return currCtx
-
-proc lookupString (contextStack: seq[Context], tagkey: string): string =
- ## Return the string associated with `tagkey` in Context `c`.
- ## If the Context at `tagkey` does not exist, return the empty string.
- result = lookupContext(contextStack, tagkey).toString()
-
-proc ignore (tag: string, tokens: seq[Token], index: int): int =
-  #ignore
-  var i = index + 1
-  var nexttoken = tokens[i]
-  var openedsections = 1
-  let lentokens = len(tokens)
-
-  while i < lentokens and openedsections > 0:
-    if nexttoken.value == tag:
-      if nexttoken.tokentype in [TokenType.section, TokenType.invertedsection]:
-        openedsections += 1
-      elif nexttoken.tokentype == TokenType.ender:
-        openedsections -= 1
-      else: discard
-    else: discard
-
-    i += 1
-    if i < lentokens:
-      nexttoken = tokens[i]
-
-  return i
-
-proc parallelReplace (str: string, substitutions: openArray[tuple[pattern: string, by: string]]): string =
-  ## Returns a modified copy of `str` with the `substitutions` applied
-  result = str
-  for sub in substitutions:
-    result = result.replace(sub[0], sub[1])
-
-proc render* (tmplate: string, contextStack: seq[Context]): string =
-  ## Take a mustache template `tmplate`, a seq of evaluation Context's
-  ## and return the rendered string. This is the main procedure.
-  let
-    htmlReplaceBy = [("&", "&amp;"),
-                     ("<", "&lt;"),
-                     (">", "&gt;"),
-                     ("\\", "&#92;"),
-                     ("\"", "&quot;")]
-
-  var renderings : seq[string] = @[]
-
-  #Object
-  var sections : seq[string] = @[]
-  var contextStack = contextStack
-
-  #Array
-  var loopStartPositions : seq[int] = @[]
-  var loopCounters : seq[int] = @[]
-
-  #Indentation
-  var indentation = ""
-
-  let tokens = toSeq(moustachu_tokenizer.tokenize(tmplate))
-  let lentokens = len(tokens)
-
-  var index = 0
-
-  while index < lentokens:
-    let token = tokens[index]
-
-    case token.tokentype
-    of TokenType.comment:
-      discard
-
-    of TokenType.escapedvariable:
-      var viewvalue = contextStack.lookupString(token.value)
-#      viewvalue = viewvalue.parallelReplace(htmlReplaceBy)
-      renderings.add(viewvalue)
-
-    of TokenType.unescapedvariable:
-      var viewvalue = contextStack.lookupString(token.value)
-      renderings.add(viewvalue)
-
-    of TokenType.section:
-      let ctx = contextStack.lookupContext(token.value)
-      if ctx == nil:
-        index = ignore(token.value, tokens, index)
-        continue
-      elif ctx.kind == CObject:
-        # enter a new section
-        if ctx.len == 0:
-          index = ignore(token.value, tokens, index)
-          continue
-        else:
-          contextStack.add(ctx)
-          sections.add(token.value)
-      elif ctx.kind == CArray:
-        # update the array loop stacks
-        if ctx.len == 0:
-          index = ignore(token.value, tokens, index)
-          continue
-        else:
-          #do looping
-          index += 1
-          loopStartPositions.add(index)
-          loopCounters.add(ctx.len)
-          sections.add(token.value)
-          contextStack.add(ctx[ctx.len - loopCounters[^1]])
-          continue
-      elif ctx.kind == CValue:
-        if not ctx:
-          index = ignore(token.value, tokens, index)
-          continue
-        else: discard #we will render the text inside the section
-
-    of TokenType.invertedsection:
-      let ctx = contextStack.lookupContext(token.value)
-      if ctx != nil:
-        if ctx.kind == CObject:
-          index = ignore(token.value, tokens, index)
-          continue
-        elif ctx.kind == CArray:
-          if ctx.len != 0:
-            index = ignore(token.value, tokens, index)
-            continue
-        elif ctx.kind == CValue:
-          if ctx:
-            index = ignore(token.value, tokens, index)
-            continue
-          else: discard #we will render the text inside the section
-
-    of TokenType.ender:
-      var ctx = contextStack.lookupContext(token.value)
-      if ctx != nil:
-        if ctx.kind == CObject:
-          discard contextStack.pop()
-          discard sections.pop()
-        elif ctx.kind == CArray:
-          if ctx.len > 0:
-            loopCounters[^1] -= 1
-            discard contextStack.pop()
-            if loopCounters[^1] == 0:
-              discard loopCounters.pop()
-              discard loopStartPositions.pop()
-              discard sections.pop()
-            else:
-              index = loopStartPositions[^1]
-              contextStack.add(ctx[ctx.len - loopCounters[^1]])
-              continue
-
-    of TokenType.indenter:
-      if token.value != "":
-        indentation = token.value
-        renderings.add(indentation)
-
-    else:
-      renderings.add(token.value)
-
-    index += 1
-
-  result = join(renderings, "")
-
-proc render* (tmplate: string, c: Context): string =
-  ## Take a mustache template `tmplate`, an evaluation Context `c`
-  ## and return the rendered string.
-  var contextStack = @[c]
-  result = tmplate.render(contextStack)
-
-proc render* (tmplate: string, jsonContext: JsonNode): string  {.exportc.} =
-  ## Take a mustache template `tmplate`, an evaluation context as a JsonNode
-  ## and return the rendered string.
-  let nc = newContext(jsonContext)
-  var contextStack = @[nc]
-  result = tmplate.render(contextStack)
diff --git a/src/libs/moustachu_context.nim b/src/libs/moustachu_context.nim
@@ -1,273 +0,0 @@
-import json, sequtils, strutils, tables
-
-type
-  ContextKind* = enum ## possible Context types
-    CArray,
-    CObject,
-    CValue
-
-  ## Context used to render a mustache template
-  Context* = ref ContextObj
-  ContextObj = object
-    case kind*: ContextKind
-    of CValue:
-      val: JsonNode
-    of CArray:
-      elems: seq[Context]
-    of CObject:
-      fields: Table[string, Context]
-
-## Builders
-
-proc newContext*(j : JsonNode = nil): Context =
-  ## Create a new Context based on a JsonNode object
-  new(result)
-  if j == nil:
-    result = Context(kind: CObject)
-    result.fields = initTable[string, Context](4)
-  else:
-    case j.kind
-    of JObject:
-      result = Context(kind: CObject)
-      result.fields = initTable[string, Context](4)
-      for key, val in pairs(j.fields):
-        result.fields[key] = newContext(val)
-    of JArray:
-      result = Context(kind: CArray)
-      result.elems = @[]
-      for val in j.elems:
-        result.elems.add(newContext(val))
-    else:
-      result = Context(kind: CValue)
-      result.val = j
-
-proc newContext*(c: Context): Context {.exportc.} =
-  ## Create a new Context based on an existing context. The new Context
-  ## is an unconnected copy of the existing context simply containing the
-  ## values of the original.
-  ## 
-  ## Some code to demonstrate:
-  ## 
-  ## .. code:: nim
-  ## 
-  ##     import moustachu
-  ## 
-  ##     var a = newContext()
-  ##     a["test"] = "original"
-  ## 
-  ##     var b = a              # copy the pointer to b
-  ##     var c = newContext(a)  # copy the content to c
-  ##
-  ##     b["test"] = "changed"
-  ## 
-  ##     echo a["test"].toString()  # -> "changed"
-  ##     echo b["test"].toString()  # -> "changed"
-  ##     echo c["test"].toString()  # -> "original"
-  new(result)
-  if c == nil:
-    result.kind = CObject
-    result.fields = initTable[string, Context](4)
-  else:
-    result.kind = c.kind
-    case c.kind
-    of CValue:
-      result.val = c.val
-    of CArray:
-      result.elems = @[]
-      for item in c.elems:
-        result.elems.add(newContext(item))
-    of CObject:
-      result.fields = initTable[string, Context](4)
-      for key, val in pairs(c.fields):
-        result.fields[key] = newContext(val)
-
-proc newArrayContext*(): Context =
-  ## Create a new Context of kind CArray
-  result = Context(kind: CArray)
-  result.elems = @[]
-
-proc internal_set(value: string): Context =
-  newContext(newJString(value))
-
-proc internal_set(value: int): Context =
-  newContext(newJInt(value))
-
-proc internal_set(value: float): Context =
-  newContext(newJFloat(value))
-
-proc internal_set(value: bool): Context =
-  newContext(newJBool(value))
-
-## ## Getters
-
-proc `[]`*(c: Context, key: string): Context =
-  ## Return the Context associated with `key`.
-  ## If the Context at `key` does not exist, return nil.
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  if c.kind != CObject: return nil
-  if c.fields.hasKey(key): return c.fields[key] else: return nil
-
-proc `[]`*(c: Context, index: int): Context =
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  if c.kind != CArray: return nil else: return c.elems[index]
-
-## Setters
-
-proc `[]=`*(c: var Context, key: string, value: Context) =
-  ## Assign a context `value` to `key` in context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c.fields[key] = value
-
-proc `[]=`*(c: var Context, key: string, value: JsonNode) =
-  ## Convert and assign `value` to `key` in `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = newContext(value)
-
-proc `[]=`*(c: var Context; key: string, value: BiggestInt) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = newContext(newJInt(value))
-
-proc `[]=`*(c: var Context; key: string, value: string) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = newContext(newJString(value))
-
-proc `[]=`*(c: var Context; key: string, value: float) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = newContext(newJFloat(value))
-
-proc `[]=`*(c: var Context; key: string, value: bool) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = newContext(newJBool(value))
-
-proc `[]=`*(c: var Context, key: string, value: openarray[Context]) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  var contextList = newArrayContext()
-  for v in value:
-    contextList.elems.add(v)
-  c[key] = contextList
-
-proc `[]=`*(c: var Context, key: string, value: openarray[string]) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = map(value, proc(x: string): Context = newContext(newJString(x)))
-
-proc `[]=`*(c: var Context, key: string, value: openarray[int]) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = map(value, proc(x: int): Context = newContext(newJInt(x)))
-
-proc `[]=`*(c: var Context, key: string, value: openarray[float]) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = map(value, proc(x: float): Context = newContext(newJFloat(x)))
-
-proc `[]=`*(c: var Context, key: string, value: openarray[bool]) =
-  ## Assign `value` to `key` in Context `c`
-  assert(c != nil, "Context is nil. Did you forget to initialize with newContext()?")
-  assert(c.kind == CObject)
-  c[key] = map(value, proc(x: bool): Context = newContext(newJBool(x)))
-
-proc add*(c: Context, value: Context) =
-  ## Add 'value' object to array.
-  assert(c.kind == CArray)
-  c.elems.add(value)
-
-proc add*[T: string, int, float, bool](c: Context, value: T) =
-  ## Add 'value' to array. Reference later with dot substitution "{{.}}"
-  assert(c.kind == CArray)
-  c.elems.add(internal_set(value))
-
-## Printers
-
-proc `$`*(c: Context): string =
-  ## Return a string representing the context. Useful for debugging
-  if c == nil:
-    result = "Context->nil"
-    return
-  result = "Context->[kind: " & $c.kind
-  case c.kind
-  of CValue:
-    if c.val.str == "":
-      result &= "\nval: "
-    else:
-      result &= "\nval: " & $c.val
-  of CArray:
-    if c.elems == @[]:
-      result &= "\nnot initialized"
-    else:
-      var strArray = map(c.elems, proc(c: Context): string = $c)
-      result &= "\nelems: [" & join(strArray, ", ") & "]"
-  of CObject:
-    var strArray : seq[string] = @[]
-    for key, val in pairs(c.fields):
-      strArray.add(key & ": " & $val)
-    result &= "\nfields: {" & join(strArray, ", ") & "}"
-  result &= "\n]"
-
-proc toString*(c: Context): string =
-  ## Return string representation of `c` relevant to mustache
-  if c != nil:
-    if c.kind == CValue:
-      case c.val.kind
-      of JString:
-        if c.val.str == "":
-          return ""
-        return c.val.str
-      of JFloat:
-       return c.val.fnum.formatFloat(ffDefault, -1)
-      of JInt:
-       return $c.val.num
-      of JNull:
-       return ""
-      of JBool:
-       return if c.val.bval: "true" else: ""
-      else:
-       return $c.val
-    else:
-      return $c
-  else:
-    return ""
-
-proc len*(c: Context): int =
-  if c.kind == CArray: 
-    result = c.elems.len
-  elif c.kind == CObject:
-    result = c.fields.len
-  else: discard
-
-converter toBool*(c: Context): bool =
-  assert(c.kind == CValue)
-  case c.val.kind
-  of JBool: result = c.val.bval
-  of JNull: result = false
-  of JString: result = c.val.str != ""
-  else: result = true
-
-proc newContext*[T: string | int | bool | float](d: openarray[tuple[key: string, value: T ]]): Context =
-  ## Create a new Context based on an array of [string, T] tuples
-  ## 
-  ## For example, you could do:
-  ##     var c = NewContext({"x": 7, "y": -20})
-  ## or,
-  ##     var c = NewContext({"r": "apple", "b": "bike"})
-  ## or, if you must:
-  ##     var c = NewContext([("r", "apple"), ("b", "tunnel")])
-  var c = newContext()
-  for entry in d:
-    c[entry.key] = entry.value
-  return c
diff --git a/src/libs/moustachu_tokenizer.nim b/src/libs/moustachu_tokenizer.nim
@@ -1,128 +0,0 @@
-import strutils
-
-type
-  TokenType* {.pure.} = enum
-    rawtext,
-    escapedvariable,
-    unescapedvariable,
-    section,
-    invertedsection,
-    comment,
-    ender,
-    indenter
-
-  Token* = tuple[tokentype: TokenType, value: string]
-
-
-proc left_side_empty(tmplate: string, pivotindex: int): tuple[empty: bool, newlineindex: int] =
-  var ls_i = 0
-  var ls_empty = false
-  var i = pivotindex - 1
-  while i > -1 and tmplate[i] in {' ', '\t'}: dec(i)
-  if (i == -1) or (tmplate[i] == '\l'):
-    ls_i = i
-    ls_empty = true
-  return (empty: ls_empty, newlineindex: ls_i)
-
-
-iterator tokenize*(tmplate: string): Token =
-  let opening = "{{"
-  var pos = 0
-
-  while pos < tmplate.len:
-    let originalpos = pos
-    var closing = "}}"
-
-    # find start of tag
-    var opening_index = tmplate.find(opening, start=pos)
-    if opening_index == -1:
-      yield (tokentype: TokenType.rawtext, value: tmplate[pos..high(tmplate)])
-      break
-
-    #Check if the left side is empty
-    var left_side = left_side_empty(tmplate, opening_index)
-    var ls_empty = left_side.empty
-    var ls_i = left_side.newlineindex
-
-    #Deal with text before tag
-    var beforetoken = (tokentype: TokenType.rawtext, value: "")
-    if opening_index > pos:
-      #safe bet for now
-      beforetoken.value = tmplate[pos..opening_index-1]
-
-    pos = opening_index + opening.len
-
-    if not (pos < tmplate.len):
-      yield (tokentype: TokenType.rawtext, value: tmplate[opening_index..high(tmplate)])
-      break
-
-    #Determine TokenType
-    var tt = TokenType.escapedvariable
-
-    case tmplate[pos]
-    of '!':
-      tt = TokenType.comment
-      pos += 1
-    of '&':
-      tt = TokenType.unescapedvariable
-      pos += 1
-    of '{':
-      tt = TokenType.unescapedvariable
-      pos += 1
-      closing &= "}"
-    of '#':
-      tt = TokenType.section
-      pos += 1
-    of '^':
-      tt = TokenType.invertedsection
-      pos += 1
-    of '/':
-      tt = TokenType.ender
-      pos += 1
-    else:
-      tt = TokenType.escapedvariable
-
-    #find end of tag
-    var closingindex = tmplate.find(closing, start=pos)
-    if closingindex == -1:
-      if beforetoken.value != "": yield beforetoken
-      yield (tokentype: TokenType.rawtext, value: tmplate[opening_index..pos-1])
-      continue
-
-    #Check if the right side is empty
-    var rs_i = 0
-    var rs_empty = false
-    var i = 0
-    if ls_empty:
-      i = closingindex + closing.len
-      while i < tmplate.len and tmplate[i] in {' ', '\t'}: inc(i)
-      if i == tmplate.len:
-        rs_i = i - 1
-        rs_empty = true
-      elif tmplate[i] == '\c' and (i+1 < tmplate.len) and (tmplate[i+1] == '\l'):
-        rs_i = i + 1
-        rs_empty = true
-      elif tmplate[i] == '\l':
-        rs_i = i
-        rs_empty = true
-      else:
-        discard
-
-    if tt in [TokenType.comment, TokenType.section,
-              TokenType.invertedsection, TokenType.ender]:
-      # Standalone tokens
-      if rs_empty:
-        if beforetoken.value != "":
-          beforetoken.value = tmplate[originalpos..ls_i]
-          yield beforetoken
-
-        yield (tokentype: tt, value: tmplate[pos..closingindex-1].strip)
-        pos = rs_i + 1 # remove new line of this line
-      else:
-        if beforetoken.value != "": yield beforetoken
-        yield (tokentype: tt, value: tmplate[pos..closingindex-1].strip)
-        pos = closingindex + closing.len
-    else:
-      if beforetoken.value != "": yield beforetoken
-      yield (tokentype: tt, value: tmplate[pos..closingindex-1].strip)
-      pos = closingindex + closing.len
diff --git a/src/libs/tiny_sqlite.nim b/src/libs/tiny_sqlite.nim
@@ -1,662 +0,0 @@
-## .. include:: ./tiny_sqlite/private/documentation.rst
-
-import std / [options, typetraits, sequtils]
-from tiny_sqlite / sqlite_wrapper as sqlite import nil
-import tiny_sqlite / private / stmtcache
-
-when not declared(tupleLen):
-    import macros
-    macro tupleLen(typ: typedesc[tuple]): int =
-        let impl = getType(typ)
-        result = newIntlitNode(impl[1].len - 1)
-
-export options.get, options.isSome, options.isNone
-
-type
-    DbConnImpl = ref object 
-        handle: sqlite.Sqlite3 ## The underlying SQLite3 handle
-        cache: StmtCache
-
-    DbConn* = distinct DbConnImpl ## Encapsulates a database connection.
-
-    SqlStatementImpl = ref object
-        handle: sqlite.Stmt
-        db: DbConn
-
-    SqlStatement* = distinct SqlStatementImpl ## A prepared SQL statement.
-
-    DbMode* = enum
-        dbRead,
-        dbReadWrite
-
-    SqliteError* = object of CatchableError ## \
-        ## Raised when whenever a database related error occurs.
-        ## Errors are typically a result of API misuse,
-        ## e.g trying to close an already closed database connection.
-
-    DbValueKind* = enum ## \
-        ## Enum of all possible value types in a SQLite database.
-        sqliteNull,
-        sqliteInteger,
-        sqliteReal,
-        sqliteText,
-        sqliteBlob
-
-    DbValue* = object ## \
-        ## Can represent any value in a SQLite database.
-        case kind*: DbValueKind
-        of sqliteInteger:
-            intVal*: int64
-        of sqliteReal:
-            floatVal*: float64
-        of sqliteText:
-            strVal*: string
-        of sqliteBlob:
-            blobVal*: seq[byte]
-        of sqliteNull:
-            discard
-
-    Rc = cint
-
-    ResultRow* = object
-        values: seq[DbValue]
-        columns: seq[string]
-
-const SqliteRcOk = [ sqlite.SQLITE_OK, sqlite.SQLITE_DONE, sqlite.SQLITE_ROW ]
-
-
-# Forward declarations
-proc isInTransaction*(db: DbConn): bool {.noSideEffect.}
-proc isOpen*(db: DbConn): bool {.noSideEffect, inline.}
-
-template handle(db: DbConn): sqlite.Sqlite3 = DbConnImpl(db).handle
-template handle(statement: SqlStatement): sqlite.Stmt = SqlStatementImpl(statement).handle
-template db(statement: SqlStatement): DbConn = SqlStatementImpl(statement).db
-template cache(db: DbConn): StmtCache = DbConnImpl(db).cache
-
-template hasCache(db: DbConn): bool = db.cache.capacity > 0
-
-template assertCanUseDb(db: DbConn) =
-    doAssert (not DbConnImpl(db).isNil) and (not db.handle.isNil), "Database is closed"
-
-template assertCanUseStatement(statement: SqlStatement, busyOk: static[bool] = false) =
-    doAssert (not SqlStatementImpl(statement).isNil) and (not statement.handle.isNil),
-        "Statement cannot be used because it has already been finalized."
-    doAssert not statement.db.handle.isNil,
-        "Statement cannot be used because the database connection has been closed"
-    when not busyOk:
-        doAssert not sqlite.stmt_busy(statement.handle),
-            "Statement cannot be used while inside the 'all' iterator"
-
-proc newSqliteError(db: DbConn): ref SqliteError =
-    ## Raises a SqliteError exception.
-    (ref SqliteError)(msg: "sqlite error: " & $sqlite.errmsg(db.handle))
-
-proc newSqliteError(msg: string): ref SqliteError =
-    ## Raises a SqliteError exception.
-    (ref SqliteError)(msg: msg)
-
-template checkRc(db: DbConn, rc: Rc) =
-    if rc notin SqliteRcOk:
-        raise newSqliteError(db)
-
-proc skipLeadingWhiteSpaceAndComments(sql: var cstring) =
-    let original = sql
-
-    template `&+`(s: cstring, offset: int): cstring =
-        cast[cstring](cast[ByteAddress](sql) + offset)
-
-    while true:
-        case sql[0]
-        of {' ', '\t', '\v', '\r', '\l', '\f'}:
-            sql = sql &+ 1
-        of '-':
-            if sql[1] == '-':
-                sql = sql &+ 2
-                while sql[0] != '\n':
-                    sql = sql &+ 1
-                    if sql[0] == '\0':
-                        return
-                sql = sql &+ 1
-            else:
-                return;
-        of '/':
-            if sql[1] == '*':
-                sql = sql &+ 2
-                while sql[0] != '*' or sql[1] != '/':
-                    sql = sql &+ 1
-                    if sql[0] == '\0':
-                        sql = original
-                        return
-                sql = sql &+ 2
-            else:
-                return;
-        else:
-            return
-
-#
-# DbValue
-#
-
-proc toDbValue*[T: Ordinal](val: T): DbValue =
-    ## Convert an ordinal value to a Dbvalue.
-    DbValue(kind: sqliteInteger, intVal: val.int64)
-
-proc toDbValue*[T: SomeFloat](val: T): DbValue =
-    ## Convert a float to a DbValue.
-    DbValue(kind: sqliteReal, floatVal: val)
-
-proc toDbValue*[T: string](val: T): DbValue =
-    ## Convert a string to a DbValue.
-    DbValue(kind: sqliteText, strVal: val)
-
-proc toDbValue*[T: seq[byte]](val: T): DbValue =
-    ## Convert a sequence of bytes to a DbValue.
-    DbValue(kind: sqliteBlob, blobVal: val)
-
-proc toDbValue*[T: Option](val: T): DbValue =
-    ## Convert an optional value to a DbValue.
-    if val.isNone:
-        DbValue(kind: sqliteNull)
-    else:
-        toDbValue(val.get)
-
-proc toDbValue*[T: type(nil)](val: T): DbValue =
-    ## Convert a nil literal to a DbValue.
-    DbValue(kind: sqliteNull)
-
-proc toDbValues*(values: varargs[DbValue, toDbValue]): seq[DbValue] =
-    ## Convert several values to a sequence of DbValue's.
-    runnableExamples:
-        doAssert toDbValues("string", 23) == @[toDbValue("string"), toDbValue(23)] 
-    @values
-
-proc fromDbValue*(value: DbValue, T: typedesc[Ordinal]): T =
-    # Convert a DbValue to an ordinal.
-    value.intval.T
-
-proc fromDbValue*(value: DbValue, T: typedesc[SomeFloat]): float64 =
-    ## Convert a DbValue to a float.
-    value.floatVal
-
-proc fromDbValue*(value: DbValue, T: typedesc[string]): string =
-    ## Convert a DbValue to a string.
-    value.strVal
-
-proc fromDbValue*(value: DbValue, T: typedesc[seq[byte]]): seq[byte] =
-    ## Convert a DbValue to a sequence of bytes.
-    value.blobVal
-
-proc fromDbValue*[T](value: DbValue, _: typedesc[Option[T]]): Option[T] =
-    ## Convert a DbValue to an optional value.
-    if value.kind == sqliteNull:
-        none(T)
-    else:
-        some(value.fromDbValue(T))
-
-proc fromDbValue*(value: DbValue, T: typedesc[DbValue]): T =
-    ## Special overload that simply return `value`.
-    ## The purpose of this overload is to do partial unpacking.
-    ## For example, if the type of one column in a result row is unknown,
-    ## the DbValue type can be kept just for that column.
-    ## 
-    ## .. code-block:: nim
-    ## 
-    ##   for row in db.iterate("SELECT name, extra FROM Person"):
-    ##       # Type of 'extra' is unknown, so we don't unpack it.
-    ##       # The 'extra' variable will be of type 'DbValue'
-    ##       let (name, extra) = row.unpack((string, DbValue))
-    value
-
-proc `$`*(dbVal: DbValue): string =
-    result.add "DbValue["
-    case dbVal.kind
-    of sqliteInteger: result.add $dbVal.intVal
-    of sqliteReal:    result.add $dbVal.floatVal
-    of sqliteText:    result.addQuoted dbVal.strVal
-    of sqliteBlob:    result.add "<blob>"
-    of sqliteNull:    result.add "nil"
-    result.add "]"
-
-proc `==`*(a, b: DbValue): bool =
-    ## Returns true if `a` and `b` represents the same value.
-    if a.kind != b.kind:
-        false
-    else:
-        case a.kind
-        of sqliteInteger: a.intVal == b.intVal
-        of sqliteReal:    a.floatVal == b.floatVal
-        of sqliteText:    a.strVal == b.strVal
-        of sqliteBlob:    a.blobVal == b.blobVal
-        of sqliteNull:    true
-
-#
-# PStmt
-#
-
-proc bindParams(db: DbConn, stmtHandle: sqlite.Stmt, params: varargs[DbValue]): Rc =
-    result = sqlite.SQLITE_OK
-    let expectedParamsLen = sqlite.bind_parameter_count(stmtHandle) 
-    if expectedParamsLen != params.len:
-        raise newSqliteError("SQL statement contains " & $expectedParamsLen &
-            " parameters but only " & $params.len & " was provided.")
-
-    var idx = 1'i32
-    for value in params:
-        let rc =
-            case value.kind
-            of sqliteNull:
-                sqlite.bind_null(stmtHandle, idx)
-            of sqliteInteger:
-                sqlite.bind_int64(stmtHandle, idx, value.intval)
-            of sqliteReal:
-                sqlite.bind_double(stmtHandle, idx, value.floatVal)
-            of sqliteText:   
-                sqlite.bind_text(stmtHandle, idx, value.strVal.cstring, value.strVal.len.int32, sqlite.SQLITE_TRANSIENT)
-            of sqliteBlob:
-                sqlite.bind_blob(stmtHandle, idx.int32, cast[string](value.blobVal).cstring,
-                    value.blobVal.len.int32, sqlite.SQLITE_TRANSIENT)
-
-        if rc notin SqliteRcOk:
-            return rc
-        idx.inc
-
-proc prepareSql(db: DbConn, sql: string): sqlite.Stmt =
-    var tail: cstring
-    let rc = sqlite.prepare_v2(db.handle, sql.cstring, sql.len.cint + 1, result, tail)
-    db.checkRc(rc)
-    tail.skipLeadingWhiteSpaceAndComments()
-    assert tail.len == 0,
-        "Only single SQL statement is allowed in this context. " &
-        "To execute several SQL statements, use 'execScript'"
-
-proc prepareSql(db: DbConn, sql: string, params: seq[DbValue]): sqlite.Stmt
-        {.raises: [SqliteError].} =
-    if db.hasCache:
-        result = db.cache.getOrDefault(sql)
-        if result.isNil:
-            result = prepareSql(db, sql)
-            db.cache[sql] = result
-    else:
-        result = prepareSql(db, sql)
-    let rc = db.bindParams(result, params)
-    db.checkRc(rc)
-
-proc readColumn(stmtHandle: sqlite.Stmt, col: int32): DbValue =
-    let columnType = sqlite.column_type(stmtHandle, col)
-    case columnType
-    of sqlite.SQLITE_INTEGER:
-        result = toDbValue(sqlite.column_int64(stmtHandle, col))
-    of sqlite.SQLITE_FLOAT:
-        result = toDbValue(sqlite.column_double(stmtHandle, col))
-    of sqlite.SQLITE_TEXT:
-        result = toDbValue($sqlite.column_text(stmtHandle, col))
-    of sqlite.SQLITE_BLOB:
-        let blob = sqlite.column_blob(stmtHandle, col)
-        let bytes = sqlite.column_bytes(stmtHandle, col)
-        var s = newSeq[byte](bytes)
-        if bytes != 0:
-            copyMem(addr(s[0]), blob, bytes)
-        result = toDbValue(s)
-    of sqlite.SQLITE_NULL:
-        result = toDbValue(nil)
-    else:
-        raiseAssert "Unexpected column type: " & $columnType
-
-iterator iterate(db: DbConn, stmtOrHandle: sqlite.Stmt | SqlStatement, params: varargs[DbValue],
-        errorRc: var int32): ResultRow =
-    let stmtHandle = when stmtOrHandle is sqlite.Stmt: stmtOrHandle else: stmtOrHandle.handle
-    errorRc = db.bindParams(stmtHandle, params)
-    if errorRc in SqliteRcOk:
-        var rowLen = sqlite.column_count(stmtHandle)
-        var columns = newSeq[string](rowLen)
-        for idx in 0 ..< rowLen:
-            columns[idx] = $sqlite.column_name(stmtHandle, idx)
-        while true:
-            var row = ResultRow(values: newSeq[DbValue](rowLen), columns: columns)
-            when stmtOrHandle is sqlite.Stmt:
-                assertCanUseDb db
-            else:
-                assertCanUseStatement stmtOrHandle, busyOk = true
-            let rc = sqlite.step(stmtHandle)
-            if rc == sqlite.SQLITE_ROW:
-                for idx in 0 ..< rowLen:
-                    row.values[idx] = readColumn(stmtHandle, idx)
-                yield row
-            elif rc == sqlite.SQLITE_DONE:
-                break
-            else:
-                errorRc = rc
-                break
-
-#
-# DbConn
-#
-
-proc exec*(db: DbConn, sql: string, params: varargs[DbValue, toDbValue]) =
-    ## Executes ``sql``, which must be a single SQL statement.
-    runnableExamples:
-        let db = openDatabase(":memory:")
-        db.exec("CREATE TABLE Person(name, age)")
-        db.exec("INSERT INTO Person(name, age) VALUES(?, ?)",
-            "John Doe", 23)
-    assertCanUseDb db
-    let stmtHandle = db.prepareSql(sql, @params)
-    let rc = sqlite.step(stmtHandle)
-    if db.hasCache:
-        discard sqlite.reset(stmtHandle)
-    else:
-        discard sqlite.finalize(stmtHandle)
-    db.checkRc(rc)
-
-template transaction*(db: DbConn, body: untyped) =
-    ## Starts a transaction and runs `body` within it. At the end the transaction is commited.
-    ## If an error is raised by `body` the transaction is rolled back. Nesting transactions is a no-op.
-    if db.isInTransaction:
-        body
-    else:
-        db.exec("BEGIN")
-        var ok = true
-        try:
-            try:
-                body
-            except Exception:
-                ok = false
-                db.exec("ROLLBACK")
-                raise
-        finally:
-            if ok:
-                db.exec("COMMIT")
-
-proc execMany*(db: DbConn, sql: string, params: seq[seq[DbValue]]) =
-    ## Executes ``sql``, which must be a single SQL statement, repeatedly using each element of
-    ## ``params`` as parameters. The statements are executed inside a transaction.
-    assertCanUseDb db
-    db.transaction:
-        for p in params:
-            db.exec(sql, p)
-
-proc execScript*(db: DbConn, sql: string) =
-    ## Executes ``sql``, which can consist of multiple SQL statements.
-    ## The statements are executed inside a transaction.
-    assertCanUseDb db
-    db.transaction:
-        var remaining = sql.cstring
-        while remaining.len > 0:
-            var tail: cstring
-            var stmtHandle: sqlite.Stmt
-            var rc = sqlite.prepare_v2(db.handle, remaining, -1, stmtHandle, tail)
-            db.checkRc(rc)
-            rc = sqlite.step(stmtHandle)
-            discard sqlite.finalize(stmtHandle)
-            db.checkRc(rc)
-            remaining = tail
-            remaining.skipLeadingWhiteSpaceAndComments()
-
-iterator iterate*(db: DbConn, sql: string,
-        params: varargs[DbValue, toDbValue]): ResultRow =
-    ## Executes ``sql``, which must be a single SQL statement, and yields each result row one by one.
-    assertCanUseDb db
-    let stmtHandle = db.prepareSql(sql, @params)
-    var errorRc: int32
-    try:
-        for row in db.iterate(stmtHandle, params, errorRc):
-            yield row
-    finally:
-        # The database might have been closed while iterating, in which
-        # case we don't need to clean up the statement.
-        if not db.handle.isNil:
-            if db.hasCache:
-                discard sqlite.reset(stmtHandle)
-            else:
-                discard sqlite.finalize(stmtHandle)
-        db.checkRc(errorRc)
-
-proc all*(db: DbConn, sql: string,
-        params: varargs[DbValue, toDbValue]): seq[ResultRow] =
-    ## Executes ``sql``, which must be a single SQL statement, and returns all result rows.
-    for row in db.iterate(sql, params):
-        result.add row
-
-proc one*(db: DbConn, sql: string,
-        params: varargs[DbValue, toDbValue]): Option[ResultRow] =
-    ## Executes `sql`, which must be a single SQL statement, and returns the first result row.
-    ## Returns `none(seq[DbValue])` if the result was empty.
-    for row in db.iterate(sql, params):
-        return some(row)
-
-proc value*(db: DbConn, sql: string,
-        params: varargs[DbValue, toDbValue]): Option[DbValue] =
-    ## Executes `sql`, which must be a single SQL statement, and returns the first column of the first result row.
-    ## Returns `none(DbValue)` if the result was empty.
-    for row in db.iterate(sql, params):
-        return some(row.values[0])
-
-proc close*(db: DbConn) =
-    ## Closes the database connection. This should be called once the connection will no longer be used
-    ## to avoid leaking memory. Closing an already closed database is a harmless no-op.
-    if not db.isOpen:
-        return
-    var stmtHandle = sqlite.next_stmt(db.handle, nil)
-    while not stmtHandle.isNil:
-        discard sqlite.finalize(stmtHandle)
-        stmtHandle = sqlite.next_stmt(db.handle, nil)
-    db.cache.clear()
-    let rc = sqlite.close(db.handle)
-    db.checkRc(rc)
-    DbConnImpl(db).handle = nil
-
-proc lastInsertRowId*(db: DbConn): int64 =
-    ## Get the row id of the last inserted row.
-    ## For tables with an integer primary key,
-    ## the row id will be the primary key.
-    ##
-    ## For more information, refer to the SQLite documentation
-    ## (https://www.sqlite.org/c3ref/last_insert_rowid.html).
-    assertCanUseDb db
-    sqlite.last_insert_rowid(db.handle)
-
-proc changes*(db: DbConn): int32 =
-    ## Get the number of changes triggered by the most recent INSERT, UPDATE or
-    ## DELETE statement.
-    ##
-    ## For more information, refer to the SQLite documentation
-    ## (https://www.sqlite.org/c3ref/changes.html).
-    assertCanUseDb db
-    sqlite.changes(db.handle)
-
-proc isReadonly*(db: DbConn): bool =
-    ## Returns true if ``db`` is in readonly mode.
-    runnableExamples:
-        let db = openDatabase(":memory:")
-        doAssert not db.isReadonly
-        let db2 = openDatabase(":memory:", dbRead)
-        doAssert db2.isReadonly
-    assertCanUseDb db
-    sqlite.db_readonly(db.handle, "main") == 1
-
-proc isOpen*(db: DbConn): bool {.inline.} =
-    ## Returns true if `db` has been opened and not yet closed.
-    runnableExamples:
-        var db: DbConn
-        doAssert not db.isOpen
-        db = openDatabase(":memory:")
-        doAssert db.isOpen
-        db.close()
-        doAssert not db.isOpen
-    (not DbConnImpl(db).isNil) and (not db.handle.isNil)
-
-proc isInTransaction*(db: DbConn): bool =
-    ## Returns true if a transaction is currently active.
-    runnableExamples:
-        let db = openDatabase(":memory:")
-        doAssert not db.isInTransaction
-        db.transaction:
-            doAssert db.isInTransaction
-    assertCanUseDb db
-    sqlite.get_autocommit(db.handle) == 0
-
-proc unsafeHandle*(db: DbConn): sqlite.Sqlite3 {.inline.} =
-    ## Returns the raw SQLite3 handle. This can be used to interact directly with the SQLite C API
-    ## with the `tiny_sqlite/sqlite_wrapper` module. Note that the handle should not be used after `db.close` has
-    ## been called as doing so would break memory safety.
-    assert not DbConnImpl(db).handle.isNil, "Database is closed"
-    DbConnImpl(db).handle
-
-#
-# SqlStatement
-#
-
-proc stmt*(db: DbConn, sql: string): SqlStatement =
-    ## Constructs a prepared statement from `sql`.
-    assertCanUseDb db
-    let handle = prepareSql(db, sql)
-    SqlStatementImpl(handle: handle, db: db).SqlStatement
-    
-proc exec*(statement: SqlStatement, params: varargs[DbValue, toDbValue]) =
-    ## Executes `statement` with `params` as parameters.
-    assertCanUseStatement statement
-    var rc = statement.db.bindParams(statement.handle, params)
-    if rc notin SqliteRcOk:
-        discard sqlite.reset(statement.handle)
-        statement.db.checkRc(rc)
-    else:
-        rc = sqlite.step(statement.handle)
-        discard sqlite.reset(statement.handle)
-        statement.db.checkRc(rc)
-
-proc execMany*(statement: SqlStatement, params: seq[seq[DbValue]]) =
-    ## Executes ``statement`` repeatedly using each element of ``params`` as parameters.
-    ## The statements are executed inside a transaction.
-    assertCanUseStatement statement
-    statement.db.transaction:
-        for p in params:
-            statement.exec(p)
-
-iterator iterate*(statement: SqlStatement, params: varargs[DbValue, toDbValue]): ResultRow =
-    ## Executes ``statement`` and yields each result row one by one.
-    assertCanUseStatement statement
-    var errorRc: int32
-    try:
-        for row in statement.db.iterate(statement, params, errorRc):
-            yield row
-    finally:
-        # The database might have been closed while iterating, in which
-        # case we don't need to clean up the statement.
-        if not statement.db.handle.isNil:
-            discard sqlite.reset(statement.handle)
-        statement.db.checkRc errorRc
-
-proc all*(statement: SqlStatement, params: varargs[DbValue, toDbValue]): seq[ResultRow] =
-    ## Executes ``statement`` and returns all result rows.
-    assertCanUseStatement statement
-    for row in statement.iterate(params):
-        result.add row
-
-proc one*(statement: SqlStatement,
-        params: varargs[DbValue, toDbValue]): Option[ResultRow] =
-    ## Executes `statement` and returns the first row found.
-    ## Returns `none(seq[DbValue])` if no result was found.
-    assertCanUseStatement statement
-    for row in statement.iterate(params):
-        return some(row)
-
-proc value*(statement: SqlStatement,
-        params: varargs[DbValue, toDbValue]): Option[DbValue] =
-    ## Executes `statement` and returns the first column of the first row found. 
-    ## Returns `none(DbValue)` if no result was found.
-    assertCanUseStatement statement
-    for row in statement.iterate(params):
-        return some(row.values[0])
-
-proc finalize*(statement: SqlStatement): void =
-    ## Finalize the statement. This needs to be called once the statement is no longer used to
-    ## prevent memory leaks. Finalizing an already finalized statement is a harmless no-op.
-    if SqlStatementImpl(statement).isNil:
-        return
-    discard sqlite.finalize(statement.handle)
-    SqlStatementImpl(statement).handle = nil
-
-proc isAlive*(statement: SqlStatement): bool =
-    ## Returns true if ``statement`` has been initialized and not yet finalized.
-    (not SqlStatementImpl(statement).isNil) and (not statement.handle.isNil) and
-        (not statement.db.handle.isNil)
-
-proc openDatabase*(path: string, mode = dbReadWrite, cacheSize: Natural = 100): DbConn =
-    ## Open a new database connection to a database file. To create an
-    ## in-memory database the special path `":memory:"` can be used.
-    ## If the database doesn't already exist and ``mode`` is ``dbReadWrite``,
-    ## the database will be created. If the database doesn't exist and ``mode``
-    ## is ``dbRead``, a ``SqliteError`` exception will be raised.
-    ##
-    ## NOTE: To avoid memory leaks, ``db.close`` must be called when the
-    ## database connection is no longer needed.
-    runnableExamples:
-        let memDb = openDatabase(":memory:")
-    var handle: sqlite.Sqlite3
-    let db = new DbConnImpl
-    db.handle = handle
-    if cacheSize > 0:
-        db.cache = initStmtCache(cacheSize)
-    result = DbConn(db)
-    case mode
-    of dbReadWrite:
-        let rc = sqlite.open(path, db.handle)
-        result.checkRc(rc)
-    of dbRead:
-        let rc = sqlite.open_v2(path, db.handle, sqlite.SQLITE_OPEN_READONLY, nil)
-        result.checkRc(rc)
-
-#
-# ResultRow
-#
-
-proc `[]`*(row: ResultRow, idx: Natural): DbValue =
-    ## Access a column in the result row based on index.
-    row.values[idx]
-
-proc `[]`*(row: ResultRow, column: string): DbValue =
-    ## Access a column in te result row based on column name.
-    ## The column name must be unambiguous.
-    let idx = row.columns.find(column)
-    assert idx != -1, "Column does not exist in row: '" & column & "'"
-    doAssert count(row.columns, column) == 1, "Column exists multiple times in row: '" & column & "'"
-    row.values[idx]
-
-proc len*(row: ResultRow): int =
-    ## Returns the number of columns in the result row.
-    row.values.len
-
-proc values*(row: ResultRow): seq[DbValue] =
-    ## Returns all column values in the result row.
-    row.values
-
-proc columns*(row: ResultRow): seq[string] =
-    ## Returns all column names in the result row.
-    row.columns
-
-proc unpack*[T: tuple](row: ResultRow, _: typedesc[T]): T =
-    ## Calls ``fromDbValue`` on each element of ``row`` and returns it
-    ## as a tuple.
-    doAssert row.len == result.typeof.tupleLen,
-        "Unpack expected a tuple with " & $row.len & " field(s) but found: " & $T
-    var idx = 0
-    for value in result.fields:
-        value = row[idx].fromDbValue(type(value))
-        idx.inc
-
-#
-# Deprecations
-#
-
-proc rows*(db: DbConn, sql: string, params: varargs[DbValue, toDbValue]): seq[seq[DbValue]]
-        {.deprecated: "use 'all' instead".} =
-    db.all(sql, params).mapIt(it.values)
-    
-iterator rows*(db: DbConn, sql: string, params: varargs[DbValue, toDbValue]): seq[DbValue]
-        {.deprecated: "use 'iterate' instead".} =
-    for row in db.all(sql, params):
-        yield row.values
-
-proc unpack*[T: tuple](row: seq[DbValue], _: typedesc[T]): T {.deprecated.} =
-    ResultRow(values: row).unpack(T)-
\ No newline at end of file
diff --git a/src/libs/tiny_sqlite/private/documentation.rst b/src/libs/tiny_sqlite/private/documentation.rst
@@ -1,182 +0,0 @@
-***********
-tiny_sqlite
-***********
-
-Opening a database connection.
-##############################
-
-A database connection is opened by calling the `openDatabase <#openDatabase,string,Natural>`_ procedure with the
-path to the database file as an argument. If the file doesn't exist, it will be created. An in-memory database can
-be created by using the special path `":memory:"` as an argument. Once the database connection is no longer needed
-`close <#close,DbConn>`_ must be called to prevent memory leaks.
-
-.. code-block:: nim
-
-    let db = openDatabase("path/to/file.db")
-    # ... (do something with `db`)
-    db.close()
-
-Executing SQL
-#############
-
-The `exec <#exec,DbConn,string,varargs[DbValue,toDbValue]>`_ procedure can be used to execute a single SQL statement.
-The `execScript <#execScript,DbConn,string>`_ procedure is used to execute several statements, but it doesn't support
-parameter substitution.
-
-.. code-block:: nim
-
-    db.execScript("""
-        CREATE TABLE Person(
-            name TEXT,
-            age INTEGER
-        );
-
-        CREATE TABLE Log(
-            message TEXT
-        );
-    """)
-
-    db.exec("""
-        INSERT INTO Person(name, age)
-        VALUES(?, ?);
-    """, "John Doe", 37)
-
-Reading data
-############
-
-Four different procedures for reading data are available:
-
-- `all <#all,DbConn,string,varargs[DbValue,toDbValue]>`_: procedure returning all result rows
-- `iterate <#iterate.i,DbConn,string,varargs[DbValue,toDbValue]>`_: iterator yielding each result row one by one
-- `one <#one,DbConn,string,varargs[DbValue,toDbValue]>`_: procedure returning the first result row, or `none` if no result row exists
-- `value <#value,DbConn,string,varargs[DbValue,toDbValue]>`_: procedure returning the first column of the first result row, or `none` if no result row exists
-
-Note that the procedures `one` and `value` returns the result wrapped in an `Option`. See the standard library
-`options module <https://nim-lang.org/docs/options.html>`_ for documentation on how to deal with `Option` values.
-For convenience the `tiny_sqlite` module exports the `options.get`, `options.isSome`, and `options.isNone` procedures so the options
-module doesn't need to be explicitly imported for typical usage.
-
-.. code-block:: nim
-
-    for row in db.iterate("SELECT name, age FROM Person"):
-        # The 'row' variable is of type ResultRow.
-        # The column values can be accesed by both index and column name:
-        echo row[0].strVal      # Prints the name
-        echo row["name"].strVal # Prints the name
-        echo row[1].intVal      # Prints the age
-        # Above we're using the raw DbValue's directly. Instead, we can unpack the
-        # DbValue using the fromDbValue procedure:
-        echo fromDbValue(row[0], string) # Prints the name
-        echo fromDbValue(row[1], int)    # Prints the age
-        # Alternatively, the entire row can be unpacked at once:
-        let (name, age) = row.unpack((string, int))
-        # Unpacking the value is preferable as it makes it possible to handle
-        # bools, enums, distinct types, nullable types and more. For example, nullable
-        # types are handled using Option[T]:
-        echo fromDbValue(row[0], Option[string]) # Will work even if the db value is NULL
-    
-    # Example of reading a single value. In this case, 'value' will be of type `Option[DbValue]`.
-    let value = db.one("SELECT age FROM Person WHERE name = ?", "John Doe")
-    if value.isSome:
-        echo fromDbValue(value.get, int) # Prints age of John Doe
-
-
-Inserting data in bulk
-######################
-
-The `exec <#exec,DbConn,string,varargs[DbValue,toDbValue]>`_ procedure works fine for inserting single rows,
-but it gets awkward when inserting many rows. For this purpose the `execMany <#execMany,DbConn,string,varargs[DbValue,toDbValue]>`_
-procedure can be used instead. It executes the same SQL repeatedly, but with different parameters each time.
-
-.. code-block:: nim
-
-    let parameters = @[toDbValues("Person 1", 17), toDbValues("Person 2", 55)]
-    # Will insert two rows
-    db.execMany("""
-        INSERT INTO Person(name, age)
-        VALUES(?, ?);
-    """, parameters)
-
-Transactions
-############
-
-The procedures that can execute multiple SQL statements (`execScript` and `execMany`) are wrapped in a transaction by
-`tiny_sqlite`. Transactions can also be controlled manually by using one of these two options:
-
-- Option 1: using the `transaction <#transaction.t,DbConn,untyped>`_ template
-
-.. code-block:: nim
-
-    db.transaction:
-        # Anything inside here is executed inside a transaction which
-        # will be rolled back in case of an error
-        db.exec("DELETE FROM Person")
-        db.exec("""INSERT INTO Person(name, age) VALUES("Jane Doe", 35)""")
-
-- Option 2: using the `exec` procedure manually
-
-.. code-block:: nim
-
-    db.exec("BEGIN")
-    try:
-        db.exec("DELETE FROM Person")
-        db.exec("""INSERT INTO Person(name, age) VALUES("Jane Doe", 35)""")
-        db.exec("COMMIT")
-    except:
-        db.exec("ROLLBACK")
-
-Prepared statements
-###################
-
-All the procedures for executing SQL described above create and execute prepared statements internally. In addition to
-those procedures, ``tiny_sqlite`` also offers an API for preparing SQL statements explicitly. Prepared statements are
-created with the `stmt <#stmt,DbConn,string>`_ procedure, and the same procedures for executing SQL that are available
-directly on the connection object are also available for the prepared statement:
-
-
-.. code-block:: nim
-    
-    let stmt = db.stmt("INSERT INTO Person(name, age) VALUES (?, ?)")
-    stmt.exec("John Doe", 21)
-    # Once the statement is no longer needed it must be finalized
-    # to prevent memory leaks.
-    stmt.finalize()
-
-There are performance benefits of reusing prepared statements, since the preparation only needs to be done once.
-However, `tiny_sqlite` keeps an internal cache of prepared statements, so it's typically not necesarry to manage
-prepared statements manually. If you prefer if `tiny_sqlite` doesn't perform this caching, you can disable it by
-setting the `cacheSize` parameter when opening the database:
-
-.. code-block:: nim
-
-    let db = openDatabase(":memory:", cacheSize = 0)
-
-Supported types
-###############
-
-For a type to be supported when using unpacking and parameter substitution the procedures `toDbValue` and `fromDbValue`
-must be implemented for the type. Below is table describing which types are supported by default and to which SQLite
-type they are mapped to:
-
-====================  =================================================================================
-Nim type              SQLite type
-====================  =================================================================================
-``Ordinal``           | ``INTEGER``
-``SomeFloat``         | ``REAL``
-``string``            | ``TEXT``
-``seq[byte]``         | ``BLOB``
-``Option[T]``         | ``NULL`` if value is ``none(T)``, otherwise the type that ``T`` would use
-====================  =================================================================================
-
-This can be extended by implementing `toDdValue`  and `fromDbValue` for other types on your own. Below is an example
-how support for `times.Time` can be added:
-
-.. code-block:: nim
-
-    import times
-
-    proc toDbValue(t: Time): DbValue =
-        DbValue(kind: sqliteInteger, intVal: toUnix(t))
-
-    proc fromDbValue(value: DbValue, T: typedesc[Time]): Time =
-        fromUnix(value.intval)
diff --git a/src/libs/tiny_sqlite/private/stmtcache.nim b/src/libs/tiny_sqlite/private/stmtcache.nim
@@ -1,81 +0,0 @@
-## Implements a least-recently-used cache for prepared statements based on
-## https://github.com/jackhftang/lrucache.nim.
-
-import std / [lists, tables]
-from .. / sqlite_wrapper as sqlite import nil
-
-type
-  Node = object
-    key: string
-    val: sqlite.Stmt
-
-  StmtCache* = object 
-    capacity: int
-    list: DoublyLinkedList[Node]
-    table: Table[string, DoublyLinkedNode[Node]]
-
-proc initStmtCache*(capacity: Natural): StmtCache =
-  ## Create a new Least-Recently-Used (LRU) cache that store the last `capacity`-accessed items.
-  StmtCache(
-    capacity: capacity,
-    list: initDoublyLinkedList[Node](),
-    table: initTable[string, DoublyLinkedNode[Node]](rightSize(capacity))
-  )
-
-proc resize(cache: var StmtCache) =
-  while cache.table.len > cache.capacity:
-    let t = cache.list.tail
-    cache.table.del(t.value.key)
-    discard sqlite.finalize(t.value.val)
-    cache.list.remove t
-
-proc capacity*(cache: StmtCache): int = 
-  ## Get the maximum capacity of cache
-  cache.capacity
-
-proc len*(cache: StmtCache): int = 
-  ## Return number of keys in cache
-  cache.table.len
-
-proc contains*(cache: StmtCache, key: string): bool =
-  ## Check whether key in cache. Does *NOT* update recentness.
-  cache.table.contains(key)
-
-proc clear*(cache: var StmtCache) =
-  ## remove all items
-  cache.list = initDoublyLinkedList[Node]()
-  cache.table.clear()
-
-proc `[]`*(cache: var StmtCache, key: string): sqlite.Stmt =
-  ## Read value from `cache` by `key` and update recentness
-  ## Raise `KeyError` if `key` is not in `cache`.
-  let node = cache.table[key]
-  result = node.value.val
-  cache.list.remove node
-  cache.list.prepend node
-
-proc `[]=`*(cache: var StmtCache, key: string, val: sqlite.Stmt) =
-  ## Put value `v` in cache with key `k`.
-  ## Remove least recently used value from cache if length exceeds capacity.
-  var node = cache.table.getOrDefault(key, nil)
-  if node.isNil:
-    let node = newDoublyLinkedNode[Node](
-      Node(key: key, val: val)
-    )
-    cache.table[key] = node
-    cache.list.prepend node
-    cache.resize()
-  else:
-    # set value 
-    node.value.val = val
-    # move to head
-    cache.list.remove node
-    cache.list.prepend node
-    
-proc getOrDefault*(cache: StmtCache, key: string, val: sqlite.Stmt = nil): sqlite.Stmt =
-  ## Similar to get, but return `val` if `key` is not in `cache`
-  let node = cache.table.getOrDefault(key, nil)
-  if node.isNil:
-    result = val
-  else:
-    result = node.value.val
diff --git a/src/libs/tiny_sqlite/sqlite_wrapper.nim b/src/libs/tiny_sqlite/sqlite_wrapper.nim
@@ -1,263 +0,0 @@
-when defined(windows):
-  when defined(nimOldDlls):
-    const Lib = "sqlite3.dll"
-  elif defined(cpu64):
-    const Lib = "sqlite3_64.dll"
-  else:
-    const Lib = "sqlite3_32.dll"
-elif defined(macosx):
-  const
-    Lib = "libsqlite3(|.0).dylib"
-else:
-  const
-    Lib = "libsqlite3.so(|.0)"
-
-type
-    Sqlite3* = ptr object
-
-    Stmt* = ptr object
-
-    Callback* = proc (p: pointer, para2: cint, para3, para4: cstringArray): cint
-        {.cdecl, raises: [].}
-
-    SqliteDestructor* = proc (p: pointer)
-        {.cdecl, locks: 0, tags: [], raises: [], gcsafe.}
-
-const
-    SQLITE_OK*         = 0.cint
-    SQLITE_ERROR*      = 1.cint # SQL error or missing database
-    SQLITE_INTERNAL*   = 2.cint # An internal logic error in SQLite
-    SQLITE_PERM*       = 3.cint # Access permission denied
-    SQLITE_ABORT*      = 4.cint # Callback routine requested an abort
-    SQLITE_BUSY*       = 5.cint # The database file is locked
-    SQLITE_LOCKED*     = 6.cint # A table in the database is locked
-    SQLITE_NOMEM*      = 7.cint # A malloc() failed
-    SQLITE_READONLY*   = 8.cint # Attempt to write a readonly database
-    SQLITE_INTERRUPT*  = 9.cint # Operation terminated by sqlite3_interrupt()
-    SQLITE_IOERR*      = 10.cint # Some kind of disk I/O error occurred
-    SQLITE_CORRUPT*    = 11.cint # The database disk image is malformed
-    SQLITE_NOTFOUND*   = 12.cint # (Internal Only) Table or record not found
-    SQLITE_FULL*       = 13.cint # Insertion failed because database is full
-    SQLITE_CANTOPEN*   = 14.cint # Unable to open the database file
-    SQLITE_PROTOCOL*   = 15.cint # Database lock protocol error
-    SQLITE_EMPTY*      = 16.cint # Database is empty
-    SQLITE_SCHEMA*     = 17.cint # The database schema changed
-    SQLITE_TOOBIG*     = 18.cint # Too much data for one row of a table
-    SQLITE_CONSTRAINT* = 19.cint # Abort due to contraint violation
-    SQLITE_MISMATCH*   = 20.cint # Data type mismatch
-    SQLITE_MISUSE*     = 21.cint # Library used incorrectly
-    SQLITE_NOLFS*      = 22.cint # Uses OS features not supported on host
-    SQLITE_AUTH*       = 23.cint # Authorization denied
-    SQLITE_FORMAT*     = 24.cint # Auxiliary database format error
-    SQLITE_RANGE*      = 25.cint # 2nd parameter to sqlite3_bind out of range
-    SQLITE_NOTADB*     = 26.cint # File opened that is not a database file
-    SQLITE_NOTICE*     = 27.cint
-    SQLITE_WARNING*    = 28.cint
-    SQLITE_ROW*        = 100.cint # sqlite3_step() has another row ready
-    SQLITE_DONE*       = 101.cint # sqlite3_step() has finished executing
-
-const
-  SQLITE_INTEGER* = 1.cint
-  SQLITE_FLOAT* = 2.cint
-  SQLITE_TEXT* = 3.cint
-  SQLITE_BLOB* = 4.cint
-  SQLITE_NULL* = 5.cint
-  SQLITE_UTF8* = 1.cint
-  SQLITE_UTF16LE* = 2.cint
-  SQLITE_UTF16BE* = 3.cint         # Use native byte order
-  SQLITE_UTF16* = 4.cint           # sqlite3_create_function only
-  SQLITE_ANY* = 5.cint             #sqlite_exec return values
-  SQLITE_COPY* = 0.cint
-  SQLITE_CREATE_INDEX* = 1.cint
-  SQLITE_CREATE_TABLE* = 2.cint
-  SQLITE_CREATE_TEMP_INDEX* = 3.cint
-  SQLITE_CREATE_TEMP_TABLE* = 4.cint
-  SQLITE_CREATE_TEMP_TRIGGER* = 5.cint
-  SQLITE_CREATE_TEMP_VIEW* = 6.cint
-  SQLITE_CREATE_TRIGGER* = 7.cint
-  SQLITE_CREATE_VIEW* = 8.cint
-  SQLITE_DELETE* = 9.cint
-  SQLITE_DROP_INDEX* = 10.cint
-  SQLITE_DROP_TABLE* = 11.cint
-  SQLITE_DROP_TEMP_INDEX* = 12.cint
-  SQLITE_DROP_TEMP_TABLE* = 13.cint
-  SQLITE_DROP_TEMP_TRIGGER* = 14.cint
-  SQLITE_DROP_TEMP_VIEW* = 15.cint
-  SQLITE_DROP_TRIGGER* = 16.cint
-  SQLITE_DROP_VIEW* = 17.cint
-  SQLITE_INSERT* = 18.cint
-  SQLITE_PRAGMA* = 19.cint
-  SQLITE_READ* = 20.cint
-  SQLITE_SELECT* = 21.cint
-  SQLITE_TRANSACTION* = 22.cint
-  SQLITE_UPDATE* = 23.cint
-  SQLITE_ATTACH* = 24.cint
-  SQLITE_DETACH* = 25.cint
-  SQLITE_ALTER_TABLE* = 26.cint
-  SQLITE_REINDEX* = 27.cint
-  SQLITE_DENY* = 1.cint
-  SQLITE_IGNORE* = 2.cint 
-  SQLITE_DETERMINISTIC* = 0x800.cint
-
-const
-  SQLITE_OPEN_READONLY* =        0x00000001.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_READWRITE* =       0x00000002.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_CREATE* =          0x00000004.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_DELETEONCLOSE* =   0x00000008.cint    #/* VFS only */
-  SQLITE_OPEN_EXCLUSIVE* =       0x00000010.cint    #/* VFS only */
-  SQLITE_OPEN_AUTOPROXY* =       0x00000020.cint    #/* VFS only */
-  SQLITE_OPEN_URI* =             0x00000040.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_MEMORY* =          0x00000080.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_MAIN_DB* =         0x00000100.cint    #/* VFS only */
-  SQLITE_OPEN_TEMP_DB* =         0x00000200.cint    #/* VFS only */
-  SQLITE_OPEN_TRANSIENT_DB* =    0x00000400.cint    #/* VFS only */
-  SQLITE_OPEN_MAIN_JOURNAL* =    0x00000800.cint    #/* VFS only */
-  SQLITE_OPEN_TEMP_JOURNAL* =    0x00001000.cint    #/* VFS only */
-  SQLITE_OPEN_SUBJOURNAL* =      0x00002000.cint    #/* VFS only */
-  SQLITE_OPEN_MASTER_JOURNAL* =  0x00004000.cint    #/* VFS only */
-  SQLITE_OPEN_NOMUTEX* =         0x00008000.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_FULLMUTEX* =       0x00010000.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_SHAREDCACHE* =     0x00020000.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_PRIVATECACHE* =    0x00040000.cint  #/* Ok for sqlite3_open_v2() */
-  SQLITE_OPEN_WAL* =             0x00080000.cint    #/* VFS only */
-
-const
-  SQLITE_STATIC* = nil
-  SQLITE_TRANSIENT* = cast[SqliteDestructor](-1)
-
-proc close*(db: Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_close".}
-
-proc exec*(db: Sqlite3, sql: cstring, cb: Callback, p: pointer, errmsg: var cstring): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_exec".}
-
-proc last_insert_rowid*(db: Sqlite3): int64
-    {.cdecl, dynlib: Lib, importc: "sqlite3_last_insert_rowid".}
-
-proc changes*(db: Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_changes".}
-
-proc total_changes*(db: Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_total_changes".}
-
-proc busy_handler*(db: Sqlite3,
-                   handler: proc (p: pointer, x: cint): cint {.cdecl.},
-                   p: pointer): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_busy_handler".}
-
-proc busy_timeout*(db: Sqlite3, ms: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_busy_timeout".}
-
-proc open*(filename: cstring, db: var Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_open".}
-
-proc open_v2*(filename: cstring, db: var Sqlite3, flags: cint, zVfsName: cstring ): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_open_v2".}
-
-proc errcode*(db: Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_errcode".}
-
-proc errmsg*(db: Sqlite3): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".}
-
-proc prepare_v2*(db: Sqlite3, zSql: cstring, nByte: cint, stmt: var Stmt,
-                pzTail: var cstring): cint
-    {.importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.}
-
-proc bind_blob*(stmt: Stmt, col: cint, value: pointer, len: cint,
-                para5: SqliteDestructor): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_blob".}
-
-proc bind_double*(stmt: Stmt, col: cint, value: float64): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_double".}
-
-proc bind_int*(stmt: Stmt, col: cint, value: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_int".}
-
-proc bind_int64*(stmt: Stmt, col: cint, value: int64): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_int64".}
-
-proc bind_null*(stmt: Stmt, col: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_null".}
-
-proc bind_text*(stmt: Stmt, col: cint, value: cstring, len: cint,
-                destructor: SqliteDestructor): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_text".}
-
-proc bind_parameter_count*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_parameter_count".}
-
-proc bind_parameter_name*(stmt: Stmt, col: cint): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_parameter_name".}
-
-proc bind_parameter_index*(stmt: Stmt, colName: cstring): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
-
-proc clear_bindings*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_clear_bindings".}
-
-proc column_count*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_count".}
-
-proc column_name*(stmt: Stmt, col: cint): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_name".}
-
-proc column_table_name*(stmt: Stmt, col: cint): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_table_name".}
-
-proc column_decltype*(stmt: Stmt, col: cint): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_decltype".}
-
-proc step*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_step".}
-
-proc data_count*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_data_count".}
-
-proc column_blob*(stmt: Stmt, col: cint): pointer
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_blob".}
-
-proc column_bytes*(stmt: Stmt, col: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_bytes".}
-
-proc column_double*(stmt: Stmt, col: cint): float64
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_double".}
-
-proc column_int*(stmt: Stmt, col: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_int".}
-
-proc column_int64*(stmt: Stmt, col: cint): int64
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_int64".}
-
-proc column_text*(stmt: Stmt, col: cint): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_text".}
-
-proc column_type*(stmt: Stmt, col: cint): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_column_type".}
-
-proc finalize*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_finalize".}
-
-proc reset*(stmt: Stmt): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_reset".}
-
-proc libversion*(): cstring
-    {.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
-
-proc libversion_number*(): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_libversion_number".}
-
-proc db_handle*(stmt: Stmt): Sqlite3
-    {.cdecl, dynlib: Lib, importc: "sqlite3_db_handle".}
-
-proc get_autocommit*(db: Sqlite3): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_get_autocommit".}
-
-proc db_readonly*(db: Sqlite3, dbname: cstring): cint
-    {.cdecl, dynlib: Lib, importc: "sqlite3_db_readonly".}
-
-proc next_stmt*(db: Sqlite3, stmt: Stmt): Stmt
-    {.cdecl, dynlib: Lib, importc: "sqlite3_next_stmt".}
-
-proc stmt_busy*(stmt: Stmt): bool
-    {.cdecl, dynlib: Lib, importc: "sqlite3_stmt_busy".}-
\ No newline at end of file
diff --git a/src/mastofetch.nim b/src/mastofetch.nim
@@ -1,5 +1,5 @@
 import std/[os, strutils, json, httpclient, times]
-import libs/tiny_sqlite
+import tiny_sqlite
 
 type
   User = object
diff --git a/src/website.nim b/src/website.nim
@@ -1,6 +1,6 @@
 import std/[os, options, cgi, times, strutils]
-import libs/tiny_sqlite
-import libs/[moustachu, moustachu_context]
+import tiny_sqlite
+import mustache
 
 write(stdout, "Content-Type: text/html; charset=UTF-8\n")
 write(stdout, "Cache-Control: no-store, must-revalidate\n")
diff --git a/src/website.tpl b/src/website.tpl
@@ -54,6 +54,6 @@
 	</head>
 	<body>
 		<a href="{{SCRIPT_URL}}"><img src="{{IMAGE_URL}}"></a>
-		<p>{{POST_CONTENT}} <a href="{{POST_URL}}" target="_blank">{{POST_DATE}}, {{POST_TIME}} Uhr</a></p>
+		<p>{{{POST_CONTENT}}} <a href="{{POST_URL}}" target="_blank">{{POST_DATE}}, {{POST_TIME}} Uhr</a></p>
 	</body>
 </html>