improved KVStorage support
This commit is contained in:
parent
8175bacfb8
commit
361d3f0b2b
@ -10,7 +10,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.4.2-SNAPSHOT"
|
version = "0.4.3-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -16,15 +16,40 @@ import kotlin.reflect.typeOf
|
|||||||
interface KVStorage {
|
interface KVStorage {
|
||||||
operator fun get(key: String): ByteArray?
|
operator fun get(key: String): ByteArray?
|
||||||
operator fun set(key: String, value: ByteArray?)
|
operator fun set(key: String, value: ByteArray?)
|
||||||
operator fun contains(key: String): Boolean
|
|
||||||
|
/**
|
||||||
|
* Check whether key is in storage.
|
||||||
|
* Default implementation uses [keys]. You may override it for performance
|
||||||
|
*/
|
||||||
|
operator fun contains(key: String): Boolean = key in keys
|
||||||
|
|
||||||
|
val keys: Set<String>
|
||||||
|
|
||||||
|
|
||||||
val keys: Collection<String>
|
/**
|
||||||
|
* Get number of object in the storage
|
||||||
|
* Default implementation uses [keys]. You may override it for performance
|
||||||
|
*/
|
||||||
|
val size: Int get() = keys.size
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all objects in the storage
|
||||||
|
* Default implementation uses [keys]. You may override it for performance
|
||||||
|
*/
|
||||||
fun clear() {
|
fun clear() {
|
||||||
for (k in keys) this[k] = null
|
for (k in keys) this[k] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation uses [keys]. You may override it for performance
|
||||||
|
*/
|
||||||
|
fun isEmpty() = size == 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation uses [keys]. You may override it for performance
|
||||||
|
*/
|
||||||
|
fun isNotEmpty() = size != 0
|
||||||
|
|
||||||
fun addAll(other: KVStorage) {
|
fun addAll(other: KVStorage) {
|
||||||
for (k in other.keys) {
|
for (k in other.keys) {
|
||||||
this[k] = other[k]
|
this[k] = other[k]
|
||||||
@ -38,6 +63,8 @@ inline operator fun <reified T> KVStorage.invoke(defaultValue: T,overrideName: S
|
|||||||
|
|
||||||
inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
|
inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
|
||||||
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
|
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
|
||||||
|
inline fun <reified T> KVStorage.optStored(defaultValue: T?=null, overrideName: String? = null) =
|
||||||
|
KVStorageDelegate<T?>(this, typeOf<T?>(), defaultValue, overrideName)
|
||||||
|
|
||||||
class KVStorageDelegate<T>(
|
class KVStorageDelegate<T>(
|
||||||
private val storage: KVStorage,
|
private val storage: KVStorage,
|
||||||
@ -110,7 +137,7 @@ class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
|
|||||||
return key in data
|
return key in data
|
||||||
}
|
}
|
||||||
|
|
||||||
override val keys: Collection<String>
|
override val keys: Set<String>
|
||||||
get() = underlying?.keys ?: data.keys
|
get() = underlying?.keys ?: data.keys
|
||||||
|
|
||||||
override fun clear() {
|
override fun clear() {
|
||||||
|
39
src/jsMain/kotlin/net/sergeych/parsec3/BrowserKVStorage.kt
Normal file
39
src/jsMain/kotlin/net/sergeych/parsec3/BrowserKVStorage.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package net.sergeych.parsec3
|
||||||
|
|
||||||
|
import net.sergeych.mp_tools.decodeBase64Compact
|
||||||
|
import net.sergeych.mp_tools.encodeToBase64Compact
|
||||||
|
import org.w3c.dom.Storage
|
||||||
|
import org.w3c.dom.set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default KV storage in browser. Use if with `localStorage` or `sessionStorage`. Uses
|
||||||
|
* prefix for storage values to not to collide with other data
|
||||||
|
*/
|
||||||
|
class BrowserKVStorage(keyPrefix: String, private val bst: Storage) : KVStorage {
|
||||||
|
|
||||||
|
private val prefix = "$keyPrefix:"
|
||||||
|
fun k(key: String) = "$prefix$key"
|
||||||
|
override fun get(key: String): ByteArray? {
|
||||||
|
return bst.getItem(k(key))?.decodeBase64Compact()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(key: String, value: ByteArray?) {
|
||||||
|
val corrected = k(key)
|
||||||
|
if (value == null)
|
||||||
|
bst.removeItem(corrected)
|
||||||
|
else
|
||||||
|
bst.set(corrected, value.encodeToBase64Compact())
|
||||||
|
}
|
||||||
|
|
||||||
|
override val keys: Set<String>
|
||||||
|
get() {
|
||||||
|
val kk = mutableListOf<String>()
|
||||||
|
for (i in 0 until bst.length) {
|
||||||
|
val k = bst.key(i) ?: break
|
||||||
|
if( k.startsWith(prefix)) {
|
||||||
|
kk += k.substring(prefix.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kk.toSet()
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package net.sergeych.parsec3
|
package net.sergeych.parsec3
|
||||||
|
|
||||||
actual fun defaultNamedStorage(name: String): KVStorage {
|
import kotlinx.browser.localStorage
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
@Suppress("unused")
|
||||||
|
actual fun defaultNamedStorage(name: String): KVStorage = BrowserKVStorage(name, localStorage)
|
36
src/jsTest/kotlin/testBrowserKVStorage.kt
Normal file
36
src/jsTest/kotlin/testBrowserKVStorage.kt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import kotlinx.browser.sessionStorage
|
||||||
|
import net.sergeych.parsec3.BrowserKVStorage
|
||||||
|
import net.sergeych.parsec3.optStored
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class testBrowserKVStorage {
|
||||||
|
@Test
|
||||||
|
fun testStorage() {
|
||||||
|
val st = BrowserKVStorage("tpr", sessionStorage)
|
||||||
|
var foo: String? by st.optStored()
|
||||||
|
assertNull(foo)
|
||||||
|
foo = "bar"
|
||||||
|
assertEquals("bar", foo)
|
||||||
|
println(st.keys)
|
||||||
|
assertTrue { "foo" in st.keys }
|
||||||
|
assertFalse { "bar" in st.keys }
|
||||||
|
var bar: Int? by st.optStored()
|
||||||
|
assertNull(bar)
|
||||||
|
bar = 42
|
||||||
|
assertEquals(42, bar)
|
||||||
|
assertTrue { "foo" in st.keys }
|
||||||
|
assertTrue { "bar" in st.keys }
|
||||||
|
assertEquals("bar", foo)
|
||||||
|
|
||||||
|
val st2 = BrowserKVStorage("tpr", sessionStorage)
|
||||||
|
val foo2: String? by st.optStored(overrideName = "foo")
|
||||||
|
assertEquals("bar", foo2)
|
||||||
|
|
||||||
|
assertTrue(st2.isNotEmpty())
|
||||||
|
assertTrue(st.isNotEmpty())
|
||||||
|
|
||||||
|
st.clear()
|
||||||
|
assertTrue(st.isEmpty())
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -97,7 +97,7 @@ internal class WsServerKtTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testWsServerReconnect() {
|
fun testWsServerReconnect() {
|
||||||
|
|
||||||
embeddedServer(Netty, port = 8080) {
|
embeddedServer(Netty, port = 8090) {
|
||||||
parsec3TransportServer(
|
parsec3TransportServer(
|
||||||
TestApiServer,
|
TestApiServer,
|
||||||
) {
|
) {
|
||||||
@ -112,7 +112,7 @@ internal class WsServerKtTest {
|
|||||||
}.start(wait = false)
|
}.start(wait = false)
|
||||||
|
|
||||||
Log.connectConsole(Log.Level.DEBUG)
|
Log.connectConsole(Log.Level.DEBUG)
|
||||||
val client = Parsec3WSClient("ws://localhost:8080/api/p3", TestApiClient) {
|
val client = Parsec3WSClient("ws://localhost:8090/api/p3", TestApiClient) {
|
||||||
on(api.bar) {
|
on(api.bar) {
|
||||||
"bar:$it"
|
"bar:$it"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user