Adding mounted store (#33)
* adding monted store * misc spelling * adding mounted store tests to suite * split out key * relaxed key initialization * always mount and lookup by path * cleaned up and reorged tests * test lookup by path * add re-exports * more re-exports * fix warnings and re-exports
This commit is contained in:
parent
446de6f978
commit
f5dadd93be
|
@ -1,3 +1,7 @@
|
|||
import ./datastore/datastore
|
||||
import ./datastore/fsds
|
||||
import ./datastore/sql
|
||||
import ./datastore/mountedds
|
||||
import ./datastore/tieredds
|
||||
|
||||
export datastore
|
||||
export datastore, fsds, mountedds, tieredds, sql
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import pkg/chronos
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/upraises
|
||||
|
||||
|
@ -28,6 +27,6 @@ method close*(self: Datastore): Future[?!void] {.base, async, locks: "unknown".}
|
|||
|
||||
method query*(
|
||||
self: Datastore,
|
||||
query: Query): Future[?!QueryIter] {.gcsafe.} =
|
||||
query: Query): Future[?!QueryIter] {.base, gcsafe.} =
|
||||
|
||||
raiseAssert("Not implemented!")
|
||||
|
|
|
@ -1,231 +1,3 @@
|
|||
import std/algorithm
|
||||
import std/hashes
|
||||
import std/oids
|
||||
import std/sequtils
|
||||
import std/strutils
|
||||
import ./key/key
|
||||
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
from pkg/stew/results as stewResults import get, isErr
|
||||
import pkg/upraises
|
||||
|
||||
export hashes
|
||||
|
||||
push: {.upraises: [].}
|
||||
|
||||
type
|
||||
Namespace* = object
|
||||
field*: string
|
||||
value*: string
|
||||
|
||||
Key* = object
|
||||
namespaces*: seq[Namespace]
|
||||
|
||||
const
|
||||
Delimiter* = ":"
|
||||
Separator* = "/"
|
||||
|
||||
# TODO: operator/s for combining string|Namespace,string|Namespace
|
||||
# TODO: lifting from ?![Namespace|Key] for various ops
|
||||
|
||||
func init*(
|
||||
T: type Namespace,
|
||||
field, value: string): ?!T =
|
||||
|
||||
if value.strip == "":
|
||||
return failure "value string must not be all whitespace or empty"
|
||||
|
||||
if value.contains(Delimiter):
|
||||
return failure "value string must not contain Delimiter \"" &
|
||||
Delimiter & "\""
|
||||
|
||||
if value.contains(Separator):
|
||||
return failure "value string must not contain Separator \"" &
|
||||
Separator & "\""
|
||||
|
||||
if field != "":
|
||||
if field.strip == "":
|
||||
return failure "field string must not be all whitespace"
|
||||
|
||||
if field.contains(Delimiter):
|
||||
return failure "field string must not contain Delimiter \"" &
|
||||
Delimiter & "\""
|
||||
|
||||
if field.contains(Separator):
|
||||
return failure "field string must not contain Separator \"" &
|
||||
Separator & "\""
|
||||
|
||||
success T(field: field, value: value)
|
||||
|
||||
func init*(T: type Namespace, id: string): ?!T =
|
||||
if id.strip == "":
|
||||
return failure "id string must not be all whitespace or empty"
|
||||
|
||||
if id.contains(Separator):
|
||||
return failure "id string must not contain Separator \"" & Separator & "\""
|
||||
|
||||
if id == Delimiter:
|
||||
return failure "value in id string \"[field]" & Delimiter &
|
||||
"[value]\" must not be empty"
|
||||
|
||||
if id.count(Delimiter) > 1:
|
||||
return failure "id string must not contain more than one Delimiter \"" &
|
||||
Delimiter & "\""
|
||||
|
||||
let
|
||||
(field, value) = block:
|
||||
let parts = id.split(Delimiter)
|
||||
if parts.len > 1:
|
||||
(parts[0], parts[^1])
|
||||
else:
|
||||
("", parts[^1])
|
||||
|
||||
T.init(field, value)
|
||||
|
||||
func id*(self: Namespace): string =
|
||||
if self.field.len > 0:
|
||||
self.field & Delimiter & self.value
|
||||
else:
|
||||
self.value
|
||||
|
||||
func hash*(namespace: Namespace): Hash =
|
||||
hash(namespace.id)
|
||||
|
||||
func `$`*(namespace: Namespace): string =
|
||||
"Namespace(" & namespace.id & ")"
|
||||
|
||||
func init*(T: type Key, namespaces: varargs[Namespace]): ?!T =
|
||||
if namespaces.len == 0:
|
||||
return failure "namespaces must contain at least one Namespace"
|
||||
|
||||
success T(namespaces: @namespaces)
|
||||
|
||||
func init*(T: type Key, namespaces: varargs[string]): ?!T =
|
||||
if namespaces.len == 0:
|
||||
return failure "namespaces must contain at least one Namespace id string"
|
||||
|
||||
success T(
|
||||
namespaces: namespaces.mapIt(
|
||||
?Namespace.init(it)
|
||||
))
|
||||
|
||||
func init*(T: type Key, keys: varargs[Key]): ?!T =
|
||||
if keys.len == 0:
|
||||
return failure "No keys provided"
|
||||
|
||||
success T(
|
||||
namespaces: keys.mapIt(it.namespaces).mapIt(it).concat)
|
||||
|
||||
func init*(T: type Key, id: string): ?!T =
|
||||
if id == "":
|
||||
return failure "id string must contain at least one Namespace"
|
||||
|
||||
if id.strip == "":
|
||||
return failure "id string must not be all whitespace"
|
||||
|
||||
let
|
||||
nsStrs = id.split(Separator).filterIt(it != "")
|
||||
|
||||
if nsStrs.len == 0:
|
||||
return failure "id string must not contain more than one Separator " &
|
||||
"\"" & Separator & "\""
|
||||
|
||||
Key.init(nsStrs)
|
||||
|
||||
func list*(self: Key): seq[Namespace] =
|
||||
self.namespaces
|
||||
|
||||
proc random*(T: type Key): string =
|
||||
$genOid()
|
||||
|
||||
template `[]`*(key: Key, x: auto): auto =
|
||||
key.namespaces[x]
|
||||
|
||||
func len*(self: Key): int =
|
||||
self.namespaces.len
|
||||
|
||||
iterator items*(key: Key): Namespace =
|
||||
for k in key.namespaces:
|
||||
yield k
|
||||
|
||||
func reversed*(self: Key): Key =
|
||||
Key(namespaces: self.namespaces.reversed)
|
||||
|
||||
func reverse*(self: Key): Key =
|
||||
self.reversed
|
||||
|
||||
func name*(self: Key): string =
|
||||
if self.len > 0:
|
||||
return self[^1].value
|
||||
|
||||
func `type`*(self: Key): string =
|
||||
if self.len > 0:
|
||||
return self[^1].field
|
||||
|
||||
func id*(self: Key): string =
|
||||
Separator & self.namespaces.mapIt(it.id).join(Separator)
|
||||
|
||||
func root*(self: Key): bool =
|
||||
self.len == 1
|
||||
|
||||
func parent*(self: Key): ?!Key =
|
||||
if self.root:
|
||||
failure "key has no parent"
|
||||
else:
|
||||
success Key(namespaces: self.namespaces[0..^2])
|
||||
|
||||
func path*(self: Key): ?!Key =
|
||||
let
|
||||
parent = ?self.parent
|
||||
|
||||
if self[^1].field == "":
|
||||
return success parent
|
||||
|
||||
let ns = parent.namespaces & @[Namespace(value: self[^1].field)]
|
||||
success Key(namespaces: ns)
|
||||
|
||||
func child*(self: Key, ns: Namespace): Key =
|
||||
Key(namespaces: self.namespaces & @[ns])
|
||||
|
||||
func `/`*(self: Key, ns: Namespace): Key =
|
||||
self.child(ns)
|
||||
|
||||
func child*(self: Key, namespaces: varargs[Namespace]): Key =
|
||||
Key(namespaces: self.namespaces & @namespaces)
|
||||
|
||||
func child*(self, key: Key): Key =
|
||||
Key(namespaces: self.namespaces & key.namespaces)
|
||||
|
||||
func `/`*(self, key: Key): Key =
|
||||
self.child(key)
|
||||
|
||||
func child*(self: Key, keys: varargs[Key]): Key =
|
||||
Key(namespaces: self.namespaces & concat(keys.mapIt(it.namespaces)))
|
||||
|
||||
func child*(self: Key, ids: varargs[string]): ?!Key =
|
||||
success self.child(ids.filterIt(it != "").mapIt( ?Key.init(it) ))
|
||||
|
||||
func relative*(self: Key, parent: Key): ?!Key =
|
||||
## Get a key relative to parent from current key
|
||||
##
|
||||
|
||||
if self.len < parent.len:
|
||||
return failure "Not a parent of this key!"
|
||||
|
||||
Key.init(self.namespaces[parent.namespaces.high..self.namespaces.high])
|
||||
|
||||
func `/`*(self: Key, id: string): ?!Key =
|
||||
self.child(id)
|
||||
|
||||
func ancestor*(self, other: Key): bool =
|
||||
if other.len <= self.len: false
|
||||
else: other.namespaces[0..<self.len] == self.namespaces
|
||||
|
||||
func descendant*(self, other: Key): bool =
|
||||
other.ancestor(self)
|
||||
|
||||
func hash*(key: Key): Hash {.inline.} =
|
||||
hash(key.id)
|
||||
|
||||
func `$`*(key: Key): string =
|
||||
"Key(" & key.id & ")"
|
||||
export key
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
import std/algorithm
|
||||
import std/hashes
|
||||
import std/oids
|
||||
import std/sequtils
|
||||
import std/strutils
|
||||
import std/strformat
|
||||
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
from pkg/stew/results as stewResults import get, isErr
|
||||
import pkg/upraises
|
||||
|
||||
import ./namespace
|
||||
|
||||
export hashes, namespace
|
||||
|
||||
push: {.upraises: [].}
|
||||
|
||||
type
|
||||
Key* = object
|
||||
namespaces*: seq[Namespace]
|
||||
|
||||
func init*(T: type Key, namespaces: varargs[Namespace]): ?!T =
|
||||
success T(namespaces: @namespaces)
|
||||
|
||||
func init*(T: type Key, namespaces: varargs[string]): ?!T =
|
||||
var self = T()
|
||||
for s in namespaces:
|
||||
self.namespaces &= s
|
||||
.split( Separator )
|
||||
.filterIt( it.len > 0 )
|
||||
.mapIt( ?Namespace.init(it) )
|
||||
|
||||
return success self
|
||||
|
||||
func init*(T: type Key, keys: varargs[Key]): ?!T =
|
||||
success T(
|
||||
namespaces: keys
|
||||
.mapIt(it.namespaces)
|
||||
.concat)
|
||||
|
||||
func list*(self: Key): seq[Namespace] =
|
||||
self.namespaces
|
||||
|
||||
proc random*(T: type Key): string =
|
||||
$genOid()
|
||||
|
||||
template `[]`*(key: Key, x: auto): auto =
|
||||
key.namespaces[x]
|
||||
|
||||
func len*(self: Key): int =
|
||||
self.namespaces.len
|
||||
|
||||
iterator items*(key: Key): Namespace =
|
||||
for k in key.namespaces:
|
||||
yield k
|
||||
|
||||
func reverse*(self: Key): Key =
|
||||
Key(namespaces: self.namespaces.reversed)
|
||||
|
||||
func value*(self: Key): string =
|
||||
if self.len > 0:
|
||||
return self[^1].value
|
||||
|
||||
func field*(self: Key): string =
|
||||
if self.len > 0:
|
||||
return self[^1].field
|
||||
|
||||
func id*(self: Key): string =
|
||||
Separator & self.namespaces.mapIt(it.id).join(Separator)
|
||||
|
||||
func root*(self: Key): bool =
|
||||
self.len == 1
|
||||
|
||||
func parent*(self: Key): ?!Key =
|
||||
if self.root:
|
||||
failure "key has no parent"
|
||||
else:
|
||||
success Key(namespaces: self.namespaces[0..^2])
|
||||
|
||||
func path*(self: Key): ?!Key =
|
||||
let
|
||||
tail =
|
||||
if self[^1].field.len > 0:
|
||||
self[^1].field
|
||||
else:
|
||||
self[^1].value
|
||||
|
||||
if self.root:
|
||||
return Key.init(tail)
|
||||
|
||||
return success Key(
|
||||
namespaces: (?self.parent).namespaces &
|
||||
@[Namespace(value: tail)])
|
||||
|
||||
func child*(self: Key, namespaces: varargs[Namespace]): Key =
|
||||
Key(namespaces: self.namespaces & @namespaces)
|
||||
|
||||
func `/`*(self: Key, ns: Namespace): Key =
|
||||
self.child(ns)
|
||||
|
||||
func child*(self: Key, keys: varargs[Key]): Key =
|
||||
Key(namespaces: self.namespaces & concat(keys.mapIt(it.namespaces)))
|
||||
|
||||
func `/`*(self, key: Key): Key =
|
||||
self.child(key)
|
||||
|
||||
func child*(self: Key, ids: varargs[string]): ?!Key =
|
||||
success self.child(ids.filterIt(it != "").mapIt( ?Key.init(it) ))
|
||||
|
||||
func `/`*(self: Key, id: string): ?!Key =
|
||||
self.child(id)
|
||||
|
||||
func relative*(self: Key, parent: Key): ?!Key =
|
||||
## Get a key relative to parent from current key
|
||||
##
|
||||
|
||||
if self.len < parent.len:
|
||||
return failure "Not a parent of this key!"
|
||||
|
||||
Key.init(self.namespaces[parent.namespaces.high..self.namespaces.high])
|
||||
|
||||
func ancestor*(self, other: Key): bool =
|
||||
if other.len <= self.len: false
|
||||
else: other.namespaces[0..<self.len] == self.namespaces
|
||||
|
||||
func descendant*(self, other: Key): bool =
|
||||
other.ancestor(self)
|
||||
|
||||
func hash*(key: Key): Hash {.inline.} =
|
||||
hash(key.id)
|
||||
|
||||
func `$`*(key: Key): string =
|
||||
key.id
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
import std/hashes
|
||||
import std/strformat
|
||||
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/upraises
|
||||
|
||||
push: {.upraises: [].}
|
||||
|
||||
const
|
||||
Delimiter* = ":"
|
||||
Separator* = "/"
|
||||
|
||||
type
|
||||
Namespace* = object
|
||||
field*: string
|
||||
value*: string
|
||||
|
||||
func init*(T: type Namespace, field, value: string): ?!T =
|
||||
if value.contains(Delimiter):
|
||||
return failure (&"value string must not contain Delimiter '{Delimiter}'")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
if value.contains(Separator):
|
||||
return failure (&"value string must not contain Separator {Separator}")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
if field.contains(Delimiter):
|
||||
return failure (&"field string must not contain Delimiter {Delimiter}")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
if field.contains(Separator):
|
||||
return failure (&"field string must not contain Separator {Separator}")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
success T(field: field, value: value)
|
||||
|
||||
func init*(T: type Namespace, id: string): ?!T =
|
||||
if id.len > 0:
|
||||
if id.contains(Separator):
|
||||
return failure (&"id string must not contain Separator {Separator}")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
if id.count(Delimiter) > 1:
|
||||
return failure (&"id string must not contain more than one {Delimiter}")
|
||||
.catch.expect("should not fail")
|
||||
|
||||
let
|
||||
(field, value) = block:
|
||||
let parts = id.split(Delimiter)
|
||||
if parts.len > 1:
|
||||
(parts[0], parts[^1])
|
||||
else:
|
||||
("", parts[^1])
|
||||
|
||||
T.init(field.strip, value.strip)
|
||||
|
||||
func id*(self: Namespace): string =
|
||||
if self.field.len > 0:
|
||||
self.field & Delimiter & self.value
|
||||
else:
|
||||
self.value
|
||||
|
||||
func hash*(namespace: Namespace): Hash =
|
||||
hash(namespace.id)
|
||||
|
||||
func `$`*(namespace: Namespace): string =
|
||||
namespace.id
|
|
@ -0,0 +1,110 @@
|
|||
import std/tables
|
||||
|
||||
import pkg/chronos
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/upraises
|
||||
|
||||
import ./key
|
||||
import ./query
|
||||
import ./datastore
|
||||
|
||||
export key, query
|
||||
|
||||
push: {.upraises: [].}
|
||||
|
||||
type
|
||||
MountedStore* = object
|
||||
store*: Datastore
|
||||
key*: Key
|
||||
|
||||
MountedDatastore* = ref object of Datastore
|
||||
stores*: Table[Key, MountedStore]
|
||||
|
||||
method mount*(self: MountedDatastore, key: Key, store: Datastore): ?!void {.base.} =
|
||||
## Mount a store on a namespace - namespaces are only `/`
|
||||
##
|
||||
|
||||
if key in self.stores:
|
||||
return failure("Key already has store mounted!")
|
||||
|
||||
self.stores.add(key, MountedStore(store: store, key: key))
|
||||
|
||||
return success()
|
||||
|
||||
func findStore*(self: MountedDatastore, key: Key): ?!MountedStore =
|
||||
## Find a store mounted under a particular key
|
||||
##
|
||||
|
||||
for (k, v) in self.stores.pairs:
|
||||
var
|
||||
mounted = key
|
||||
|
||||
while mounted.len > 0:
|
||||
if ?k.path == ?mounted.path:
|
||||
return success v
|
||||
|
||||
if mounted.parent.isErr:
|
||||
break
|
||||
|
||||
mounted = mounted.parent.get
|
||||
|
||||
failure newException(DatastoreKeyNotFound, "No datastore found for key")
|
||||
|
||||
proc dispatch(
|
||||
self: MountedDatastore,
|
||||
key: Key): ?!tuple[store: MountedStore, relative: Key] =
|
||||
## Helper to retrieve the store and corresponding relative key
|
||||
##
|
||||
|
||||
let
|
||||
mounted = ?self.findStore(key)
|
||||
|
||||
return success (store: mounted, relative: ?key.relative(mounted.key))
|
||||
|
||||
method contains*(
|
||||
self: MountedDatastore,
|
||||
key: Key): Future[?!bool] {.async.} =
|
||||
|
||||
without mounted =? self.dispatch(key):
|
||||
return failure "No mounted datastore found"
|
||||
|
||||
return (await mounted.store.store.contains(mounted.relative))
|
||||
|
||||
method delete*(
|
||||
self: MountedDatastore,
|
||||
key: Key): Future[?!void] {.async.} =
|
||||
|
||||
without mounted =? self.dispatch(key), error:
|
||||
return failure(error)
|
||||
|
||||
return (await mounted.store.store.delete(mounted.relative))
|
||||
|
||||
method get*(
|
||||
self: MountedDatastore,
|
||||
key: Key): Future[?!seq[byte]] {.async.} =
|
||||
|
||||
without mounted =? self.dispatch(key), error:
|
||||
return failure(error)
|
||||
|
||||
return await mounted.store.store.get(mounted.relative)
|
||||
|
||||
method put*(
|
||||
self: MountedDatastore,
|
||||
key: Key,
|
||||
data: seq[byte]): Future[?!void] {.async.} =
|
||||
|
||||
without mounted =? self.dispatch(key), error:
|
||||
return failure(error)
|
||||
|
||||
return (await mounted.store.store.put(mounted.relative, data))
|
||||
|
||||
func new*(
|
||||
T: type MountedDatastore,
|
||||
stores: Table[Key, Datastore] = initTable[Key, Datastore]()): ?!T =
|
||||
|
||||
var self = T()
|
||||
for (k, v) in stores.pairs:
|
||||
self.stores.add(?k.path, MountedStore(store: v, key: k))
|
||||
|
||||
success self
|
|
@ -7,7 +7,7 @@ import pkg/sqlite3_abi
|
|||
from pkg/stew/results as stewResults import isErr
|
||||
import pkg/upraises
|
||||
|
||||
import ./datastore
|
||||
import ../datastore
|
||||
import ./sqlitedsdb
|
||||
|
||||
export datastore, sqlitedsdb
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import std/options
|
||||
import std/sequtils
|
||||
import std/algorithm
|
||||
|
||||
import pkg/unittest2
|
||||
import pkg/questionable
|
||||
|
@ -7,112 +9,118 @@ import pkg/questionable/results
|
|||
import ../../datastore/key
|
||||
|
||||
suite "Namespace":
|
||||
test "init failure":
|
||||
test "should fail init":
|
||||
check:
|
||||
Namespace.init("a", "").isFailure
|
||||
Namespace.init("a", " ").isFailure
|
||||
Namespace.init("a:b:c").isFailure
|
||||
Namespace.init(":", "b").isFailure
|
||||
Namespace.init("a", ":").isFailure
|
||||
Namespace.init("a", "/").isFailure
|
||||
Namespace.init(":", "b").isFailure
|
||||
Namespace.init("/", "b").isFailure
|
||||
Namespace.init(" ", "b").isFailure
|
||||
Namespace.init("").isFailure
|
||||
Namespace.init(" ").isFailure
|
||||
Namespace.init("/").isFailure
|
||||
Namespace.init(":").isFailure
|
||||
Namespace.init("a:b:c").isFailure
|
||||
Namespace.init("a:").isFailure
|
||||
Namespace.init("a: ").isFailure
|
||||
Namespace.init(" :b").isFailure
|
||||
|
||||
test "init success":
|
||||
test "should succeed":
|
||||
check:
|
||||
Namespace.init(" :b").isSuccess
|
||||
Namespace.init("a: ").isSuccess
|
||||
Namespace.init("a", "").isSuccess
|
||||
Namespace.init("a", " ").isSuccess
|
||||
Namespace.init(" ", "b").isSuccess
|
||||
Namespace.init("a:").isSuccess
|
||||
Namespace.init("").isSuccess
|
||||
Namespace.init(" ").isSuccess
|
||||
Namespace.init(":").isSuccess
|
||||
Namespace.init("", "b").isSuccess
|
||||
Namespace.init("a", "b").isSuccess
|
||||
Namespace.init("a").isSuccess
|
||||
Namespace.init("a:b").isSuccess
|
||||
Namespace.init(":b").isSuccess
|
||||
|
||||
test "accessors":
|
||||
var
|
||||
ns: Namespace
|
||||
|
||||
ns = Namespace.init("", "b").tryGet()
|
||||
test "should init with value":
|
||||
let
|
||||
ns = Namespace.init("", "b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field == ""
|
||||
|
||||
ns = Namespace.init("a", "b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field != "" and ns.field == "a"
|
||||
|
||||
ns = Namespace.init(":b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field == ""
|
||||
|
||||
ns = Namespace.init("a:b").tryGet()
|
||||
test "should init with field and value":
|
||||
let
|
||||
ns = Namespace.init("a", "b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field == "a"
|
||||
|
||||
test "equality":
|
||||
test "should init with value from id string":
|
||||
let
|
||||
ns = Namespace.init(":b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field == ""
|
||||
|
||||
test "should init with field and value from id string":
|
||||
let
|
||||
ns = Namespace.init("a:b").tryGet()
|
||||
|
||||
check:
|
||||
ns.value == "b"
|
||||
ns.field == "a"
|
||||
|
||||
test "should equal":
|
||||
check:
|
||||
Namespace.init("a").tryGet() == Namespace.init("a").tryGet()
|
||||
Namespace.init("a").tryGet() != Namespace.init("b").tryGet()
|
||||
Namespace.init("", "b").tryGet() == Namespace.init("", "b").tryGet()
|
||||
Namespace.init("", "b").tryGet() == Namespace.init("b").tryGet()
|
||||
Namespace.init(":b").tryGet() == Namespace.init("b").tryGet()
|
||||
Namespace.init("", "b").tryGet() != Namespace.init("", "a").tryGet()
|
||||
Namespace.init("", "b").tryGet() != Namespace.init("a").tryGet()
|
||||
Namespace.init(":b").tryGet() != Namespace.init("a").tryGet()
|
||||
Namespace.init("a", "b").tryGet() == Namespace.init("a", "b").tryGet()
|
||||
Namespace.init("a", "b").tryGet() == Namespace.init("a:b").tryGet()
|
||||
Namespace.init("a:b").tryGet() == Namespace.init("a:b").tryGet()
|
||||
|
||||
test "should not equal":
|
||||
check:
|
||||
Namespace.init("a").tryGet() != Namespace.init("b").tryGet()
|
||||
Namespace.init("", "b").tryGet() != Namespace.init("", "a").tryGet()
|
||||
Namespace.init("", "b").tryGet() != Namespace.init("a").tryGet()
|
||||
Namespace.init(":b").tryGet() != Namespace.init("a").tryGet()
|
||||
Namespace.init("a", "b").tryGet() != Namespace.init("b", "a").tryGet()
|
||||
Namespace.init("a", "b").tryGet() != Namespace.init("b:a").tryGet()
|
||||
Namespace.init("a:b").tryGet() != Namespace.init("b:a").tryGet()
|
||||
Namespace.init("a").tryGet() != Namespace.init("a:b").tryGet()
|
||||
|
||||
test "serialization":
|
||||
var
|
||||
ns: Namespace
|
||||
|
||||
ns = Namespace.init(":b").tryGet()
|
||||
test "should return id from value string":
|
||||
let
|
||||
ns = Namespace.init(":b").tryGet()
|
||||
|
||||
check:
|
||||
ns.id == "b"
|
||||
$ns == "Namespace(" & ns.id & ")"
|
||||
$ns == ns.id
|
||||
|
||||
ns = Namespace.init("a:b").tryGet()
|
||||
test "should init id from field and value string":
|
||||
let
|
||||
ns = Namespace.init("a:b").tryGet()
|
||||
|
||||
check:
|
||||
ns.id == "a:b"
|
||||
$ns == "Namespace(" & ns.id & ")"
|
||||
$ns == ns.id
|
||||
|
||||
suite "Key":
|
||||
test "init failure":
|
||||
|
||||
check:
|
||||
Key.init("", "").isFailure
|
||||
Key.init(@[""]).isFailure
|
||||
Key.init(@[":"]).isFailure
|
||||
Key.init(@["/"]).isFailure
|
||||
Key.init("").isFailure
|
||||
Key.init(" ").isFailure
|
||||
Key.init("/").isFailure
|
||||
Key.init("///").isFailure
|
||||
Key.init(":").isFailure
|
||||
Key.init("::").isFailure
|
||||
Key.init("a:").isFailure
|
||||
Key.init("a:b/c:").isFailure
|
||||
|
||||
test "init success":
|
||||
check:
|
||||
Key.init(@["/"]).isSuccess
|
||||
Key.init("a:b/c:").isSuccess
|
||||
Key.init("a:").isSuccess
|
||||
Key.init(":").isSuccess
|
||||
Key.init(@[":"]).isSuccess
|
||||
Key.init("").isSuccess
|
||||
Key.init(" ").isSuccess
|
||||
Key.init("/").isSuccess
|
||||
Key.init("///").isSuccess
|
||||
Key.init(Namespace.init("a").tryGet()).isSuccess
|
||||
Key.init(@["a:b"]).isSuccess
|
||||
Key.init(":b").isSuccess
|
||||
|
@ -135,20 +143,14 @@ suite "Key":
|
|||
|
||||
key.list == key.namespaces
|
||||
|
||||
test "equality":
|
||||
test "should equal":
|
||||
check:
|
||||
Key.init(Namespace.init("a:b").tryGet(), Namespace.init("c").tryGet()).tryGet() == Key.init("a:b/c").tryGet()
|
||||
Key.init("a:b", "c").tryGet() == Key.init("a:b/c").tryGet()
|
||||
Key.init("a:b/c").tryGet() == Key.init("a:b/c").tryGet()
|
||||
Key.init(Namespace.init("a:b").tryGet(), Namespace.init("c").tryGet()).tryGet() != Key.init("c:b/a").tryGet()
|
||||
Key.init("a:b", "c").tryGet() != Key.init("c:b/a").tryGet()
|
||||
Key.init("a:b/c").tryGet() != Key.init("c:b/a").tryGet()
|
||||
Key.init("a:b/c").tryGet() == Key.init("/a:b/c/").tryGet()
|
||||
Key.init("a:b/c").tryGet() == Key.init("///a:b///c///").tryGet()
|
||||
Key.init("a:b/c").tryGet() != Key.init("///a:b///d///").tryGet()
|
||||
Key.init("a").tryGet() != Key.init("a:b").tryGet()
|
||||
Key.init("a").tryGet() != Key.init("a/b").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a/b").tryGet()
|
||||
Key.init("a:X/b/c").tryGet() == Key.init("a:X/b/c").tryGet()
|
||||
Key.init("a/b:X/c").tryGet() == Key.init("a/b:X/c").tryGet()
|
||||
Key.init("a/b/c:X").tryGet() == Key.init("a/b/c:X").tryGet()
|
||||
|
@ -156,6 +158,15 @@ suite "Key":
|
|||
Key.init("a:X/b:X/c").tryGet() == Key.init("a:X/b:X/c").tryGet()
|
||||
Key.init("a/b:X/c:X").tryGet() == Key.init("a/b:X/c:X").tryGet()
|
||||
Key.init("a:X/b:X/c:X").tryGet() == Key.init("a:X/b:X/c:X").tryGet()
|
||||
|
||||
test "should not equal":
|
||||
check:
|
||||
Key.init("a:b", "c").tryGet() != Key.init("c:b/a").tryGet()
|
||||
Key.init("a:b/c").tryGet() != Key.init("c:b/a").tryGet()
|
||||
Key.init("a:b/c").tryGet() != Key.init("///a:b///d///").tryGet()
|
||||
Key.init("a").tryGet() != Key.init("a:b").tryGet()
|
||||
Key.init("a").tryGet() != Key.init("a/b").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a/b").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a:X/b/c").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a/b:X/c").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a/b/c:X").tryGet()
|
||||
|
@ -164,9 +175,10 @@ suite "Key":
|
|||
Key.init("a/b/c").tryGet() != Key.init("a/b:X/c:X").tryGet()
|
||||
Key.init("a/b/c").tryGet() != Key.init("a:X/b:X/c:X").tryGet()
|
||||
|
||||
test "helpers":
|
||||
test "random key":
|
||||
check: Key.random.len == 24
|
||||
|
||||
test "key index":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
|
@ -174,14 +186,14 @@ suite "Key":
|
|||
key[1] == Namespace.init("c").tryGet()
|
||||
key[1..^1] == @[Namespace.init("c").tryGet(), Namespace.init("d:e").tryGet()]
|
||||
key[^1] == Namespace.init("d:e").tryGet()
|
||||
key.len == key.namespaces.len
|
||||
|
||||
check: key.len == key.namespaces.len
|
||||
test "key iterator":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
var
|
||||
nss: seq[Namespace]
|
||||
|
||||
for ns in key:
|
||||
nss.add ns
|
||||
nss = key.mapIt( it )
|
||||
|
||||
check:
|
||||
nss == @[
|
||||
|
@ -190,81 +202,107 @@ suite "Key":
|
|||
Namespace.init("d:e").tryGet()
|
||||
]
|
||||
|
||||
test "key reversed":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
key.reversed.namespaces == @[
|
||||
key.reverse.namespaces == @[
|
||||
Namespace.init("d:e").tryGet(),
|
||||
Namespace.init("c").tryGet(),
|
||||
Namespace.init("a:b").tryGet()
|
||||
]
|
||||
|
||||
key.reverse == key.reversed
|
||||
key.reverse.namespaces == key.namespaces.reversed
|
||||
|
||||
check: key.name == "e"
|
||||
check:
|
||||
key.reverse.value == "b"
|
||||
key.reverse.field == "a"
|
||||
|
||||
test "key root":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
Key.init(":b").tryGet().root
|
||||
not Key.init(":b/c").tryGet().root
|
||||
|
||||
test "key parent":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
Key.init(":b").?parent.isFailure
|
||||
Key.init(":b").?parent.isFailure
|
||||
|
||||
key.parent.tryGet() == Key.init("a:b/c").tryGet()
|
||||
key.parent.?parent.tryGet() == Key.init("a:b").tryGet()
|
||||
key.parent.?parent.?parent.isFailure
|
||||
|
||||
test "key path":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
key.parent.?path.tryGet() == Key.init("a:b").tryGet()
|
||||
key.path.tryGet() == Key.init("a:b/c/d").tryGet()
|
||||
Key.init("a:b/c").?path.tryGet() == Key.init("a:b").tryGet()
|
||||
key.path.tryGet() == Key.init("/a:b/c/d").tryGet()
|
||||
key.parent.?path.tryGet() == Key.init("a:b/c").tryGet()
|
||||
|
||||
Key.init("a:b/c:d").?path.tryGet() == Key.init("a:b/c").tryGet()
|
||||
Key.init("a:b/c/d:e").?path.tryGet() == Key.init("a:b/c/d").tryGet()
|
||||
|
||||
check: key.child(Namespace.init("f:g").tryGet()) == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
|
||||
check: key / Namespace.init("f:g").tryGet() == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
|
||||
var
|
||||
emptyNss: seq[Namespace]
|
||||
test "key child":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
key.child(emptyNss) == key
|
||||
key.child(Namespace.init("f:g").tryGet(), Namespace.init("h:i").tryGet()) ==
|
||||
Key.init("a:b/c/d:e/f:g/h:i").tryGet()
|
||||
key.child(Namespace.init("f:g").tryGet()) == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
key.child(newSeq[Namespace]()) == key
|
||||
key.child(
|
||||
Namespace.init("f:g").tryGet(),
|
||||
Namespace.init("h:i").tryGet()) == Key.init("a:b/c/d:e/f:g/h:i").tryGet()
|
||||
|
||||
check:
|
||||
key.child(Key.init("f:g").tryGet()) == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
key / Key.init("f:g").tryGet() == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
key.child(newSeq[Key]()) == key
|
||||
|
||||
var
|
||||
emptyKeys: seq[Key]
|
||||
key.child(
|
||||
Key.init("f:g").tryGet(),
|
||||
Key.init("h:i").tryGet()) == Key.init("a:b/c/d:e/f:g/h:i").tryGet()
|
||||
|
||||
check:
|
||||
key.child(emptyKeys) == key
|
||||
key.child(Key.init("f:g").tryGet(), Key.init("h:i").tryGet()) ==
|
||||
Key.init("a:b/c/d:e/f:g/h:i").tryGet()
|
||||
|
||||
check:
|
||||
key.child("f:g", ":::").isFailure
|
||||
key.child("f:g", "h:i").tryGet() == Key.init("a:b/c/d:e/f:g/h:i").tryGet()
|
||||
key.child("").tryGet() == key
|
||||
key.child("", "", "").tryGet() == key
|
||||
|
||||
test "key / operator":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
key / Namespace.init("f:g").tryGet() == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
(key / "").tryGet() == key
|
||||
(key / "f:g").tryGet() == Key.init("a:b/c/d:e/f:g").tryGet()
|
||||
|
||||
test "key ancestor":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
not key.ancestor(Key.init("f:g").tryGet())
|
||||
key.ancestor(key / Key.init("f:g").tryGet())
|
||||
|
||||
test "key descendant":
|
||||
let
|
||||
key = Key.init("/a:b/c/d:e").tryGet()
|
||||
|
||||
check:
|
||||
key.descendant(key.parent.tryGet())
|
||||
not Key.init("f:g").tryGet().descendant(key.parent.tryGet())
|
||||
|
||||
test "serialization":
|
||||
test "key serialization":
|
||||
let
|
||||
idStr = "/a:b/c/d:e"
|
||||
key = Key.init(idStr).tryGet()
|
||||
|
||||
check:
|
||||
key.id == idStr
|
||||
$key == "Key(" & key.id & ")"
|
||||
$key == key.id
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import std/options
|
||||
import std/os
|
||||
import std/tables
|
||||
|
||||
import pkg/asynctest
|
||||
import pkg/chronos
|
||||
import pkg/stew/results
|
||||
import pkg/stew/byteutils
|
||||
|
||||
import pkg/datastore/mountedds
|
||||
import pkg/datastore/sql
|
||||
import pkg/datastore/fsds
|
||||
|
||||
import ./dscommontests
|
||||
|
||||
suite "Test Basic Mounted Datastore":
|
||||
let
|
||||
root = "tests" / "test_data"
|
||||
(path, _, _) = instantiationInfo(-1, fullPaths = true) # get this file's name
|
||||
rootAbs = path.parentDir / root
|
||||
|
||||
key = Key.init("a:b/c/d:e").get
|
||||
sqlKey = Key.init("/root/sql").tryGet
|
||||
fsKey = Key.init("/root/fs").tryGet
|
||||
|
||||
bytes = "some bytes".toBytes
|
||||
otherBytes = "some other bytes".toBytes
|
||||
|
||||
var
|
||||
sql: SQLiteDatastore
|
||||
fs: FSDatastore
|
||||
mountedDs: MountedDatastore
|
||||
|
||||
setupAll:
|
||||
removeDir(rootAbs)
|
||||
require(not dirExists(rootAbs))
|
||||
createDir(rootAbs)
|
||||
|
||||
sql = SQLiteDatastore.new(Memory).tryGet
|
||||
fs = FSDatastore.new(rootAbs, depth = 5).tryGet
|
||||
mountedDs = MountedDatastore.new({
|
||||
sqlKey: Datastore(sql),
|
||||
fsKey: Datastore(fs)}.toTable)
|
||||
.tryGet
|
||||
|
||||
teardownAll:
|
||||
removeDir(rootAbs)
|
||||
require(not dirExists(rootAbs))
|
||||
|
||||
suite "Mounted sql":
|
||||
basicStoreTests(mountedDs, Key.init(sqlKey, key).tryGet, bytes, otherBytes)
|
||||
|
||||
suite "Mounted fs":
|
||||
basicStoreTests(mountedDs, Key.init(fsKey, key).tryGet, bytes, otherBytes)
|
||||
|
||||
suite "Test Mounted Datastore":
|
||||
|
||||
test "Should mount datastore":
|
||||
let
|
||||
ds = SQLiteDatastore.new(Memory).tryGet
|
||||
mounted = MountedDatastore.new().tryGet
|
||||
key = Key.init("/sql").tryGet
|
||||
|
||||
mounted.mount(key, ds).tryGet
|
||||
|
||||
check: mounted.stores.len == 1
|
||||
mounted.stores.withValue(key, store):
|
||||
check:
|
||||
store.key == key
|
||||
store.store == ds
|
||||
|
||||
test "Should find with exact key":
|
||||
let
|
||||
ds = SQLiteDatastore.new(Memory).tryGet
|
||||
key = Key.init("/sql").tryGet
|
||||
mounted = MountedDatastore.new({key: Datastore(ds)}.toTable).tryGet
|
||||
store = mounted.findStore(key).tryGet
|
||||
|
||||
check:
|
||||
store.key == key
|
||||
store.store == ds
|
||||
|
||||
test "Should find with child key":
|
||||
let
|
||||
ds = SQLiteDatastore.new(Memory).tryGet
|
||||
key = Key.init("/sql").tryGet
|
||||
childKey = Key.init("/sql/child/key").tryGet
|
||||
mounted = MountedDatastore.new({key: Datastore(ds)}.toTable).tryGet
|
||||
store = mounted.findStore(childKey).tryGet
|
||||
|
||||
check:
|
||||
store.key == key
|
||||
store.store == ds
|
||||
|
||||
test "Should error on missing key":
|
||||
let
|
||||
ds = SQLiteDatastore.new(Memory).tryGet
|
||||
key = Key.init("/sql").tryGet
|
||||
childKey = Key.init("/nomatchkey/child/key").tryGet
|
||||
mounted = MountedDatastore.new({key: Datastore(ds)}.toTable).tryGet
|
||||
|
||||
expect DatastoreKeyNotFound:
|
||||
discard mounted.findStore(childKey).tryGet
|
||||
|
||||
test "Should find nested stores":
|
||||
let
|
||||
ds1 = SQLiteDatastore.new(Memory).tryGet
|
||||
ds2 = SQLiteDatastore.new(Memory).tryGet
|
||||
key1 = Key.init("/sql").tryGet
|
||||
key2 = Key.init("/sql/nested").tryGet
|
||||
|
||||
nestedKey1 = Key.init("/sql/anotherkey").tryGet
|
||||
nestedKey2 = Key.init("/sql/nested/key").tryGet
|
||||
|
||||
mounted = MountedDatastore.new({
|
||||
key1: Datastore(ds1),
|
||||
key2: Datastore(ds2)}.toTable).tryGet
|
||||
|
||||
store1 = mounted.findStore(nestedKey1).tryGet
|
||||
store2 = mounted.findStore(nestedKey2).tryGet
|
||||
|
||||
check:
|
||||
store1.key == key1
|
||||
store1.store == ds1
|
||||
|
||||
store2.key == key2
|
||||
store2.store == ds2
|
||||
|
||||
test "Should find with field:value key":
|
||||
let
|
||||
ds = SQLiteDatastore.new(Memory).tryGet
|
||||
key = Key.init("/sql").tryGet
|
||||
findKey1 = Key.init("/sql:name1").tryGet
|
||||
findKey2 = Key.init("/sql:name2").tryGet
|
||||
mounted = MountedDatastore.new({key: Datastore(ds)}.toTable).tryGet
|
||||
|
||||
for k in @[findKey1, findKey2]:
|
||||
let
|
||||
store = mounted.findStore(k).tryGet
|
||||
|
||||
check:
|
||||
store.key == key
|
||||
store.store == ds
|
|
@ -12,7 +12,7 @@ import pkg/datastore/tieredds
|
|||
|
||||
import ./dscommontests
|
||||
|
||||
suite "Test Basic FSDatastore":
|
||||
suite "Test Basic Tired Datastore":
|
||||
let
|
||||
bytes = "some bytes".toBytes
|
||||
otherBytes = "some other bytes".toBytes
|
||||
|
|
|
@ -3,6 +3,7 @@ import
|
|||
./datastore/testdatastore,
|
||||
./datastore/testfsds,
|
||||
./datastore/testsql,
|
||||
./datastore/testtieredds
|
||||
./datastore/testtieredds,
|
||||
./datastore/testmountedds
|
||||
|
||||
{.warning[UnusedImport]: off.}
|
||||
|
|
Loading…
Reference in New Issue