Skip to content

Commit

Permalink
Refactor kstore-file api to remove string filePaths
Browse files Browse the repository at this point in the history
  • Loading branch information
xxfast committed Nov 30, 2023
1 parent a50f476 commit 3468cda
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 57 deletions.
6 changes: 3 additions & 3 deletions kstore-file/api/android/kstore-file.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
public final class io/github/xxfast/kstore/file/FileCodec : io/github/xxfast/kstore/Codec {
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;)V
public fun <init> (Lokio/Path;Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;)V
public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun encode (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand All @@ -9,8 +9,8 @@ public final class io/github/xxfast/kstore/file/extensions/KVersionedStoreKt {
}

public final class io/github/xxfast/kstore/file/extensions/VersionedCodec : io/github/xxfast/kstore/Codec {
public fun <init> (Ljava/lang/String;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lokio/Path;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Lokio/Path;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun encode (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand Down
6 changes: 3 additions & 3 deletions kstore-file/api/desktop/kstore-file.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
public final class io/github/xxfast/kstore/file/FileCodec : io/github/xxfast/kstore/Codec {
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;)V
public fun <init> (Lokio/Path;Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;)V
public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun encode (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand All @@ -9,8 +9,8 @@ public final class io/github/xxfast/kstore/file/extensions/KVersionedStoreKt {
}

public final class io/github/xxfast/kstore/file/extensions/VersionedCodec : io/github/xxfast/kstore/Codec {
public fun <init> (Ljava/lang/String;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lokio/Path;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Lokio/Path;ILkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun encode (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand Down
2 changes: 1 addition & 1 deletion kstore-file/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(project(":kstore"))
implementation(libs.okio)
api(libs.okio)
implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.serialization.json.okio)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,26 @@ import kotlinx.serialization.json.okio.decodeFromBufferedSource as decode
import kotlinx.serialization.json.okio.encodeToBufferedSink as encode

public inline fun <reified T : @Serializable Any> FileCodec(
filePath: String,
file: Path,
json: Json = DefaultJson,
): FileCodec<T> = FileCodec(
filePath = filePath,
file = file,
json = json,
serializer = json.serializersModule.serializer(),
)

@OptIn(ExperimentalSerializationApi::class)
public class FileCodec<T : @Serializable Any>(
filePath: String,
private val file: Path,
private val json: Json,
private val serializer: KSerializer<T>,
) : Codec<T> {
private val path: Path = filePath.toPath()

override suspend fun decode(): T? =
try { json.decode(serializer, FILE_SYSTEM.source(path).buffer()) }
try { json.decode(serializer, FILE_SYSTEM.source(file).buffer()) }
catch (e: FileNotFoundException) { null }

override suspend fun encode(value: T?) {
val parentFolder: Path? = path.parent
if (parentFolder != null && !FILE_SYSTEM.exists(parentFolder))
FILE_SYSTEM.createDirectories(parentFolder, mustCreate = false)
if (value != null) FILE_SYSTEM.sink(path).buffer().use { json.encode(serializer, value, it) }
else FILE_SYSTEM.delete(path)
if (value != null) FILE_SYSTEM.sink(file).buffer().use { json.encode(serializer, value, it) }
else FILE_SYSTEM.delete(file)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import io.github.xxfast.kstore.DefaultJson
import io.github.xxfast.kstore.KStore
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okio.Path

/**
* Creates a store with [FileCodec]
*
* @param filePath path to the file that is managed by this store
* @param file path to the file that is managed by this store
* @param default returns this value if the file is not found. defaults to null
* @param enableCache maintain a cache. If set to false, it always reads from disk
* @param json Serializer to use. defaults to [DefaultJson]
*
* @return store that contains a value of type [T]
*/
public inline fun <reified T : @Serializable Any> storeOf(
filePath: String,
file: Path,
default: T? = null,
enableCache: Boolean = true,
json: Json = DefaultJson,
): KStore<T> = KStore(
default = default,
enableCache = enableCache,
codec = FileCodec(filePath, json)
codec = FileCodec(file, json)
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okio.Path

/**
* Creates a store that contains a list
*
* @param filePath path to the file that is managed by this store
* @param file path to the file that is managed by this store
* @param default returns this value if the file is not found. defaults to empty list
* @param enableCache maintain a cache. If set to false, it always reads from disk
* @param json Serializer to use. Defaults serializer ignores unknown keys and encodes the defaults
*
* @return store that contains a list of type [T]
*/
public inline fun <reified T : @Serializable Any> listStoreOf(
filePath: String,
file: Path,
default: List<T> = emptyList(),
enableCache: Boolean = true,
json: Json = Json { ignoreUnknownKeys = true; encodeDefaults = true },
): KStore<List<T>> =
storeOf(filePath, default, enableCache, json)
storeOf(file, default, enableCache, json)
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ import kotlinx.serialization.json.okio.encodeToBufferedSink as encode
* @return store that contains a value of type [T]
*/
public inline fun <reified T : @Serializable Any> storeOf(
filePath: String,
file: Path,
version: Int,
default: T? = null,
enableCache: Boolean = true,
json: Json = Json { ignoreUnknownKeys = true; encodeDefaults = true },
noinline migration: Migration<T> = DefaultMigration(default),
): KStore<T> {
val codec: Codec<T> = VersionedCodec(filePath, version, json, json.serializersModule.serializer(), migration)
val codec: Codec<T> = VersionedCodec(file, version, json, json.serializersModule.serializer(), migration)
return KStore(default, enableCache, codec)
}

Expand All @@ -51,24 +51,23 @@ public typealias Migration<T> = (version: Int?, JsonElement?) -> T?

@OptIn(ExperimentalSerializationApi::class)
public class VersionedCodec<T: @Serializable Any>(
filePath: String,
private val file: Path,
private val version: Int = 0,
private val json: Json,
private val serializer: KSerializer<T>,
private val migration: Migration<T>,
): Codec<T> {
private val dataPath: Path = filePath.toPath()
private val versionPath: Path = "$filePath.version".toPath() // TODO: Save to file metadata instead
private val versionPath: Path = "$${file.name}.version".toPath() // TODO: Save to file metadata instead

override suspend fun decode(): T? =
try {
json.decode(serializer, FILE_SYSTEM.source(dataPath).buffer())
json.decode(serializer, FILE_SYSTEM.source(file).buffer())
} catch (e: SerializationException) {
val previousVersion: Int =
if (FILE_SYSTEM.exists(versionPath)) json.decode(Int.serializer(), FILE_SYSTEM.source(versionPath).buffer())
else 0

val data: JsonElement = json.decode(FILE_SYSTEM.source(dataPath).buffer())
val data: JsonElement = json.decode(FILE_SYSTEM.source(file).buffer())
migration(previousVersion, data)
} catch (e: FileNotFoundException) {
null
Expand All @@ -77,10 +76,10 @@ public class VersionedCodec<T: @Serializable Any>(
override suspend fun encode(value: T?) {
if (value != null) {
FILE_SYSTEM.sink(versionPath).buffer().use { json.encode(Int.serializer(), version, it) }
FILE_SYSTEM.sink(dataPath).buffer().use { json.encode(serializer, value, it) }
FILE_SYSTEM.sink(file).buffer().use { json.encode(serializer, value, it) }
} else {
FILE_SYSTEM.delete(versionPath)
FILE_SYSTEM.delete(dataPath)
FILE_SYSTEM.delete(file)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,29 @@ import kotlinx.serialization.SerializationException
import okio.Path.Companion.toPath
import okio.buffer
import okio.use
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.*

import kotlinx.serialization.json.okio.decodeFromBufferedSource as decode
import kotlinx.serialization.json.okio.encodeToBufferedSink as decode

class FileCodecTests {
private val codec: FileCodec<Cat> = FileCodec(filePath = FILE)
private val codec: FileCodec<Cat> = FileCodec(file = FILE_PATH.toPath())

@OptIn(ExperimentalSerializationApi::class)
private var stored: Cat?
get() = FILE_SYSTEM.source(FILE.toPath()).buffer().use { DefaultJson.decode(it) }
get() = FILE_SYSTEM.source(FILE_PATH.toPath()).buffer().use { DefaultJson.decode(it) }
set(value) {
FILE_SYSTEM.sink(FILE.toPath()).buffer().use { DefaultJson.decode(value, it) }
FILE_SYSTEM.sink(FILE_PATH.toPath()).buffer().use { DefaultJson.decode(value, it) }
}

@BeforeTest
fun setup(){
FILE_SYSTEM.createDirectory(FOLDER.toPath())
}

@AfterTest
fun cleanUp() {
FILE_SYSTEM.delete(FILE.toPath())
FILE_SYSTEM.delete(FILE_PATH.toPath())
}

@Test
Expand All @@ -49,7 +51,7 @@ class FileCodecTests {

@Test
fun testEncodeDecodeFromDirectory() = runTest {
val dirCodec: FileCodec<Cat> = FileCodec(filePath = "$FOLDER/$FILE")
val dirCodec: FileCodec<Cat> = FileCodec(file = "$FOLDER/$FILE_PATH".toPath())
dirCodec.encode(MYLO)
val expect: Pet = MYLO
val actual: Pet? = dirCodec.decode()
Expand All @@ -58,7 +60,7 @@ class FileCodecTests {

@Test
fun testDecodeMalformedFile() = runTest {
FILE_SYSTEM.sink(FILE.toPath()).buffer().use { it.writeUtf8("💩") }
FILE_SYSTEM.sink(FILE_PATH.toPath()).buffer().use { it.writeUtf8("💩") }
assertFailsWith<SerializationException> { codec.decode() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ data class Cat(
internal val MYLO = Cat(name = "Mylo", age = 1)
internal val OREO = Cat(name = "Oreo", age = 1)

const val FILE = "test.json"
const val FILE_PATH = "test.json"
const val FOLDER = "build/bin/tests"
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import kotlinx.coroutines.test.runTest
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.okio.encodeToBufferedSink
import okio.Path
import okio.Path.Companion.toPath
import okio.buffer
import okio.use
Expand All @@ -28,12 +29,12 @@ import kotlin.test.Test
import kotlin.test.assertEquals

class KListStoreTests {
private val filePath: String = "test_lists.json"
private val store: KStore<List<Cat>> = listStoreOf(filePath = filePath)
private val file: Path = "test_lists.json".toPath()
private val store: KStore<List<Cat>> = listStoreOf(file = file)

@AfterTest
fun setup() {
FILE_SYSTEM.delete(filePath.toPath())
FILE_SYSTEM.delete(file)
}

@Test
Expand All @@ -53,17 +54,17 @@ class KListStoreTests {

@Test
fun testReadDefault() = runTest {
val defaultStore: KStore<List<Cat>> = listStoreOf(filePath = filePath, default = listOf(MYLO))
val defaultStore: KStore<List<Cat>> = listStoreOf(file = file, default = listOf(MYLO))
val expect: List<Cat> = listOf(MYLO)
val actual: List<Cat> = defaultStore.getOrEmpty()
assertEquals(expect, actual)
}

@Test
fun testReadPreviouslyStoredList() = runTest {
FILE_SYSTEM.sink(filePath.toPath()).buffer().use { Json.encodeToBufferedSink(listOf(OREO) , it) }
FILE_SYSTEM.sink(file).buffer().use { Json.encodeToBufferedSink(listOf(OREO) , it) }
// Mylo will never be sent 😿 because there is already a stored value
val newStore: KStore<List<Cat>> = listStoreOf(filePath = filePath, default = listOf(MYLO))
val newStore: KStore<List<Cat>> = listStoreOf(file = file, default = listOf(MYLO))
val expect: List<Cat> = listOf(OREO)
val actual: List<Cat> = newStore.getOrEmpty()
assertEquals(expect, actual)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import okio.Path
import okio.Path.Companion.toPath
import kotlin.test.AfterTest
import kotlin.test.Test
Expand All @@ -25,14 +26,14 @@ val MYLO_V2 = CatV2(name = "mylo", lives = 7, age = 2, kawaiiness = 12L)
val MYLO_V3 = CatV3(name = "mylo", lives = 7, age = 2, isCute = true)

class KVersionedStoreTests {
private val filePath: String = "test_migration.json"
private val file: Path = "test_migration.json".toPath()

private val storeV0: KStore<CatV0> = storeOf(filePath = filePath)
private val storeV0: KStore<CatV0> = storeOf(file = file)

private val storeV1: KStore<CatV1> = storeOf(filePath = filePath, version = 1)
private val storeV1: KStore<CatV1> = storeOf(file = file, version = 1)

private val storeV2: KStore<CatV2> = storeOf(
filePath = filePath,
file = file,
version = 2
) { version, jsonElement ->
when (version) {
Expand All @@ -49,7 +50,7 @@ class KVersionedStoreTests {
}

private val storeV3: KStore<CatV3> = storeOf(
filePath = filePath,
file = file,
version = 3
) { version, jsonElement ->
when (version) {
Expand All @@ -75,8 +76,8 @@ class KVersionedStoreTests {

@AfterTest
fun cleanup() {
FILE_SYSTEM.delete(filePath.toPath())
FILE_SYSTEM.delete("$filePath.version".toPath())
FILE_SYSTEM.delete(file)
FILE_SYSTEM.delete("${file.name}.version".toPath())
}

@Test
Expand Down

0 comments on commit 3468cda

Please sign in to comment.