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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user