Simple persistence layer for kotlin objects
Find a file
2025-11-14 23:38:55 +01:00
gradle/wrapper init 2025-11-14 23:13:43 +01:00
src rename store() -> untypedStore() 2025-11-14 23:38:45 +01:00
test init 2025-11-14 23:13:43 +01:00
.gitignore init 2025-11-14 23:13:43 +01:00
build.gradle.kts init 2025-11-14 23:13:43 +01:00
gradle.properties init 2025-11-14 23:13:43 +01:00
gradlew init 2025-11-14 23:13:43 +01:00
gradlew.bat init 2025-11-14 23:13:43 +01:00
LICENSE readme/license 2025-11-14 23:38:55 +01:00
README.md readme/license 2025-11-14 23:38:55 +01:00
settings.gradle.kts init 2025-11-14 23:13:43 +01:00

Store

A simple persistence layer for arbitrary, typed and untyped kotlin data. It utilizes kotlinx-serialization and a flexible driver system.

Usage

Typed data

// Define some data
@Serializable
data class User(val id: Int, val name: String, val age: Int)

val store = typedStore<User>(
    // Use FsStoreDriver to save data to disk
    driver = FsStoreDriver(Path("./store")),
    // Set a namespace to allow for segmentation while using the same driver
    namespace = "typed:",
    // Set your preferred serialization format
    format = Json
)

// Persist your first data

val bob = User(id = 1, name = "bob", age = 30)

store["user.${user.id}"] = bob // thats it!

// access the data again
println(store["user.${user.id}"]!!.name) // -> "bob" 

// check when it was last accessed
println(store.age("${user.id}")) // -> A epoch timestamp pointing to the last operation on Bob

// remove data
store -= "user.${user.id}"

// and clear all of it 
store.clear()

Untyped data

val store = untypedStore(
    // MemStoreDriver will not persist or write the data and simply hold it in memory until the store is GC-ed
    driver = MemStoreDriver(),
    // You can still segment it tho
    namespace = "untyped:",
    // And use another of your favourite data formats, thanks to ktx.serialization!
    format = Toml,
)

// Persist some random data you have
store<String>["test.string"] = "Hello, World!"
store<Boolean>["test.bool"] = true

println(store<Boolean>["test.bool"]) // -> "true"
println(store<Boolean>["test.string"]) // this will fail!

// This also works with more complex elements as long as they are @Serializable
@Serializable
data class Person(val name: String)

@Serializable 
data class Dog(val type: String, val name: String, val owner: Person)

store<Dog>["dog.stella"] = Dog("corgy", "Stella", Person("Dave"))

Defining your own Driver

A driver is a simple interface that handles the actual storage of the data that is being persisted. Drivers do not concern themselves with what data they get, simply with how they will store and access it


class ConcurrentMemDriver : StoreDriver {
    private val map = ConcurrentHashMap<String, Pair<Long, String>>()
    
    fun has(key: String): Boolean = map.contains(key)
    fun remove(key: String): Boolean = map.remove(key) != null
    fun age(key: String): Long? = map[key]?.first
    fun get(key: String): String? = map[key]?.second
    fun set(key: String, value: String) = map.set(key, value)
    fun all(): Set<String> = map.keys
}

License

[MIT], go nuts