Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic open datasources so that each project don't need their own #205

Merged
merged 12 commits into from
Dec 6, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ public final class com/mirego/trikot/datasources/flow/extensions/DataStateFlowEx
public static final fun withPreviousValue (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
}

public final class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun internalRead (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun save (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lcom/mirego/trikot/datasources/flow/FlowDataSourceExpiringValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun save (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class com/mirego/trikot/datasources/flow/storage/FlowDataSourceFileManager {
public abstract fun deleteFile (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getFileAsString (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ public final class com/mirego/trikot/datasources/flow/extensions/DataStateFlowEx
public static final fun withPreviousValue (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
}

public final class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun internalRead (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun save (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lcom/mirego/trikot/datasources/flow/FlowDataSourceExpiringValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun save (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class com/mirego/trikot/datasources/flow/storage/FlowDataSourceFileManager {
public abstract fun deleteFile (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getFileAsString (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
6 changes: 6 additions & 0 deletions trikot-datasources/datasources-flow/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ kotlin {
}
}

val jsMain by getting {
dependencies {
implementation("com.squareup.okio:okio-nodefilesystem:${Versions.OKIO}")
}
}

val jsTest by getting {
dependsOn(commonTest)
dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mirego.trikot.datasources.flow

import kotlinx.coroutines.Dispatchers

internal actual object DataSourceDispatchers {
actual val IO = Dispatchers.IO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mirego.trikot.datasources.flow.filesystem

import okio.FileSystem

internal actual object NativeFileSystem {
actual val fileSystem: FileSystem = FileSystem.SYSTEM
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mirego.trikot.datasources.flow

import kotlinx.coroutines.CoroutineDispatcher

@Suppress("NO_ACTUAL_FOR_EXPECT")
internal expect object DataSourceDispatchers {
val IO: CoroutineDispatcher
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mirego.trikot.datasources.flow.filesystem

import okio.FileSystem

@Suppress("NO_ACTUAL_FOR_EXPECT")
internal expect object NativeFileSystem {
val fileSystem: FileSystem
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.mirego.trikot.datasources.flow.generic

import com.mirego.trikot.datasources.flow.BaseExpiringExecutableFlowDataSource
import com.mirego.trikot.datasources.flow.DataSourceDispatchers
import com.mirego.trikot.datasources.flow.ExpiringFlowDataSourceRequest
import com.mirego.trikot.datasources.flow.FlowDataSourceExpiringValue
import com.mirego.trikot.datasources.flow.filesystem.NativeFileSystem
import kotlinx.coroutines.withContext
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import okio.FileNotFoundException
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath

class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
private val json: Json,
private val dataSerializer: KSerializer<T>,
private val diskCachePath: String,
private val fileManager: FileSystem = NativeFileSystem.fileSystem
) : BaseExpiringExecutableFlowDataSource<R, T>() {

override suspend fun internalRead(request: R): FlowDataSourceExpiringValue<T> {
return withContext(DataSourceDispatchers.IO) {
val filePath = buildFilePath(request)
try {
val data = json.decodeFromString(dataSerializer, getFileAsString(filePath))
FlowDataSourceExpiringValue(
value = data,
expiredEpoch = (NativeFileSystem.fileSystem.metadata(filePath).lastModifiedAtMillis ?: 0) + request.expiredInMilliseconds
)
} catch (exception: SerializationException) {
fileManager.deleteRecursively(filePath)
throw exception
}
}
}

override suspend fun save(request: R, data: FlowDataSourceExpiringValue<T>?) {
data?.value?.let { dataToSave ->
withContext(DataSourceDispatchers.IO) {
try {
val filePath = buildFilePath(request)
saveStringValueToFile(filePath, json.encodeToString(dataSerializer, dataToSave))
} catch (exception: Throwable) {
println("Failed to save json to disk cache. Error: $exception")
}
}
}
}

private fun getFileAsString(filePath: Path) = if (fileManager.exists(filePath)) {
fileManager.read(filePath) {
readUtf8()
}
} else {
throw FileNotFoundException("File at path $filePath does not exist")
}

private fun saveStringValueToFile(filePath: Path, value: String) {
filePath.parent?.let { fileManager.createDirectories(it) }
fileManager.write(filePath) {
writeUtf8(value)
}
}

private fun buildFilePath(request: R) = "$diskCachePath/${request.cacheableId}.json".toPath()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mirego.trikot.datasources.flow

import kotlinx.coroutines.newSingleThreadContext

internal actual object DataSourceDispatchers {
actual val IO = newSingleThreadContext("IO").limitedParallelism(100)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mirego.trikot.datasources.flow.filesystem

import okio.FileSystem

internal actual object NativeFileSystem {
actual val fileSystem: FileSystem = FileSystem.SYSTEM
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mirego.trikot.datasources.flow

import kotlinx.coroutines.Dispatchers

internal actual object DataSourceDispatchers {
actual val IO = Dispatchers.Default
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mirego.trikot.datasources.flow.filesystem

import okio.FileSystem
import okio.NodeJsFileSystem

internal actual object NativeFileSystem {
actual val fileSystem: FileSystem = NodeJsFileSystem
}