53 lines
2.0 KiB
Kotlin

package com.mkfunproj.mobilemkch.data
import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
class Cache(private val context: Context) {
private val json = Json
private val dir: File by lazy { File(context.cacheDir, "mkch_cache").apply { mkdirs() } }
suspend fun <T> set(key: String, value: T, encoder: (T) -> String, ttlSeconds: Long) = withContext(Dispatchers.IO) {
val file = File(dir, safeName(key))
val wrapper = CacheWrapper(System.currentTimeMillis(), ttlSeconds, encoder(value))
file.writeText(json.encodeToString(wrapper))
}
suspend fun <T> get(key: String, decoder: (String) -> T): T? = withContext(Dispatchers.IO) {
val file = File(dir, safeName(key))
if (!file.exists()) return@withContext null
val wrapper = runCatching { json.decodeFromString(CacheWrapper.serializer(), file.readText()) }.getOrNull()
?: return@withContext null
val ageSec = (System.currentTimeMillis() - wrapper.timestampMs) / 1000
if (ageSec > wrapper.ttlSeconds) return@withContext null
return@withContext decoder(wrapper.payload)
}
suspend fun <T> getStale(key: String, decoder: (String) -> T): T? = withContext(Dispatchers.IO) {
val file = File(dir, safeName(key))
if (!file.exists()) return@withContext null
val wrapper = runCatching { json.decodeFromString(CacheWrapper.serializer(), file.readText()) }.getOrNull()
?: return@withContext null
return@withContext decoder(wrapper.payload)
}
suspend fun delete(key: String) = withContext(Dispatchers.IO) {
File(dir, safeName(key)).delete()
}
private fun safeName(key: String) = key.hashCode().toString(16) + ".json"
}
@kotlinx.serialization.Serializable
private data class CacheWrapper(
val timestampMs: Long,
val ttlSeconds: Long,
val payload: String
)