Skip to content

Commit

Permalink
✨ Implementing the ConfigManager (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
guiyanakuang authored Nov 20, 2023
1 parent 18dd599 commit 7042d43
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 20 deletions.
3 changes: 2 additions & 1 deletion composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import org.jetbrains.compose.ExperimentalComposeLibrary

plugins {
alias(libs.plugins.kotlinMultiplatform)

alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.kotlinSerialization)
}

kotlin {
Expand All @@ -26,6 +26,7 @@ kotlin {
implementation(compose.material)
@OptIn(ExperimentalComposeLibrary::class)
implementation(compose.components.resources)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1")
}

val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ fun ClipeveryApp(dependencies: Dependencies) {
fun ClipeveryCommon(dependencies: Dependencies) {
CompositionLocalProvider(
LocalClipeveryServer provides dependencies.clipServer,
LocalConfigManager provides dependencies.configManager
LocalConfigManager provides dependencies.configManager,
LocalFilePersist provides dependencies.filePersist
) {
ClipeveryWithProvidedDependencies()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.clipevery

import androidx.compose.runtime.staticCompositionLocalOf
import com.clipevery.config.ConfigManager
import com.clipevery.net.ClipServer
import com.clipevery.net.ConfigManager
import com.clipevery.presist.FilePersist

abstract class Dependencies {
abstract val clipServer: ClipServer
abstract val configManager: ConfigManager
abstract val filePersist: FilePersist
}

internal val LocalClipeveryServer = staticCompositionLocalOf<ClipServer> {
Expand All @@ -17,6 +19,10 @@ internal val LocalConfigManager = staticCompositionLocalOf<ConfigManager> {
noLocalProvidedFor("ConfigManager")
}

internal val LocalFilePersist = staticCompositionLocalOf<FilePersist> {
noLocalProvidedFor("FilePersist")
}

private fun noLocalProvidedFor(name: String): Nothing {
error("CompositionLocal $name not present")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.clipevery.config

import com.clipevery.AppConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

abstract class ConfigManager(private val ioScope: CoroutineScope) {

lateinit var config: AppConfig

fun initConfig(): ConfigManager {
config = try {
loadConfig() ?: AppConfig()
} catch (e: Exception) {
AppConfig()
}
return this
}

abstract fun loadConfig(): AppConfig?

@Synchronized
fun updateBindingState(bindingState: Boolean) {
config = config.copy(bindingState = bindingState)
ioScope.launch {
saveConfig(config)
}
}

@Synchronized
fun saveConfig(config: AppConfig) {
saveConfigImpl(config)
}

protected abstract fun saveConfigImpl(config: AppConfig)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.clipevery.config

enum class ConfigType {
USER,
SYSTEM
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package com.clipevery.net

import com.clipevery.AppConfig

interface ClipServer {
}

interface ConfigManager {
val config: AppConfig
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.clipevery.path

import com.clipevery.config.ConfigType
import java.nio.file.Path

interface PathProvider {
fun resolve(configName: String, configType: ConfigType): Path {
return when (configType) {
ConfigType.USER -> resolveUser(configName)
ConfigType.SYSTEM -> resolveApp(configName)
}
}

fun resolveUser(configName: String): Path

fun resolveApp(configName: String): Path
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,16 @@ expect fun currentPlatform(): Platform
interface Platform {
val name: String
val version: String

fun isWindows(): Boolean {
return name == "Windows"
}

fun isMacos(): Boolean {
return name == "Macos"
}

fun isLinux(): Boolean {
return name == "Linux"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.clipevery.presist

import com.clipevery.config.ConfigType
import com.clipevery.path.PathProvider
import java.nio.file.Path

interface FilePersist {

val pathProvider: PathProvider

fun getPersist(configName: String, configType: ConfigType): OneFilePersist {
return createOneFilePersist(pathProvider.resolve(configName, configType))
}

fun createOneFilePersist(path: Path): OneFilePersist
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.clipevery.presist

import kotlin.reflect.KClass

interface OneFilePersist {
fun <T : Any> readAs(clazz: KClass<T>): T?
fun <T> save(config: T)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.clipevery.path

import com.clipevery.platform.currentPlatform
import java.nio.file.Path
import java.nio.file.Paths

fun getPathProvider(): PathProvider {
return if (currentPlatform().isWindows()) {
WindowsPathProvider()
} else if (currentPlatform().isMacos()) {
MacosPathProvider()
} else if (currentPlatform().isLinux()) {
LinuxPathProvider()
} else {
throw IllegalStateException("Unknown platform: ${currentPlatform().name}")
}
}


class WindowsPathProvider: PathProvider {

private val userHomePath = System.getProperty("user.home")

private val userDir = System.getProperty("user.dir")

override fun resolveUser(configName: String): Path {
return Paths.get(userHomePath).resolve(".clipevery").resolve(configName)
}

override fun resolveApp(configName: String): Path {
return Paths.get(userDir).resolve(configName)
}
}


class MacosPathProvider: PathProvider {

private val userHomePath = System.getProperty("user.home")

private val userDir = System.getProperty("user.dir")

override fun resolveUser(configName: String): Path {
return Paths.get(userHomePath).resolve(".clipevery").resolve(configName)
}

override fun resolveApp(configName: String): Path {
return Paths.get(userDir).resolve(configName)
}
}

class LinuxPathProvider: PathProvider {

override fun resolveUser(configName: String): Path {
TODO("Not yet implemented")
}

override fun resolveApp(configName: String): Path {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.clipevery.presist

import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import java.nio.file.Path
import kotlin.reflect.KClass

@Suppress("UNCHECKED_CAST")
class DesktopOneFilePersist(private val path: Path) : OneFilePersist {
override fun <T: Any> readAs(clazz: KClass<T>): T? {
val file = path.toFile()
return if (file.exists()) {
val serializer = Json.serializersModule.serializer(clazz.java)
Json.decodeFromString(serializer, file.readText()) as T
} else {
null
}
}

override fun <T> save(config: T) {
val kClass = config!!::class
val serializer = Json.serializersModule.serializer(kClass.java)
val json = Json.encodeToString(serializer, config)
val file = path.toFile()
file.parentFile?.mkdirs()
file.writeText(json)
}
}
38 changes: 29 additions & 9 deletions composeApp/src/desktopMain/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.painterResource
Expand All @@ -9,19 +7,25 @@ import androidx.compose.ui.window.application
import com.clipevery.AppConfig
import com.clipevery.ClipeveryApp
import com.clipevery.Dependencies
import com.clipevery.config.ConfigManager
import com.clipevery.config.ConfigType
import com.clipevery.net.ClipServer
import com.clipevery.net.ConfigManager
import com.clipevery.path.PathProvider
import com.clipevery.path.getPathProvider
import com.clipevery.presist.DesktopOneFilePersist
import com.clipevery.presist.FilePersist
import com.clipevery.presist.OneFilePersist
import com.clipevery.utils.ioDispatcher
import kotlinx.coroutines.CoroutineScope
import java.nio.file.Path
import kotlin.system.exitProcess


fun main() = application {
val ioScope = rememberCoroutineScope { ioDispatcher }
val bingingState = remember { mutableStateOf(false) }

val dependencies = remember {
getDependencies(ioScope, bingingState)
getDependencies(ioScope)
}

Tray(icon = painterResource("clipevery_icon.png"),
Expand All @@ -43,15 +47,31 @@ fun main() = application {
}

private fun getDependencies(
ioScope: CoroutineScope,
bingingState: MutableState<Boolean>
ioScope: CoroutineScope
) = object : Dependencies() {
override val clipServer: ClipServer = object : ClipServer {
}

override val configManager: ConfigManager = object : ConfigManager {
override val config: AppConfig = AppConfig(false) // todo: read from disk config file
override val filePersist: FilePersist = object : FilePersist {
override val pathProvider: PathProvider = getPathProvider()

override fun createOneFilePersist(path: Path): OneFilePersist {
return DesktopOneFilePersist(path)
}
}

override val configManager: ConfigManager = object : ConfigManager(ioScope) {

val configFilePersist = filePersist.getPersist("appConfig.json", ConfigType.USER)

override fun loadConfig(): AppConfig? {
return configFilePersist.readAs(AppConfig::class)
}

override fun saveConfigImpl(config: AppConfig) {
configFilePersist.save(config)
}
}.initConfig()
}

//@Preview
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.clipevery.path

import org.junit.Test
import kotlin.test.assertEquals

class PathProviderTest {

@Test
fun testPathProvider() {
val pathProvider = getPathProvider()

val configPath = pathProvider.resolveUser("test.config")

assertEquals("test.config", configPath.fileName.toString())
assertEquals(".clipevery", configPath.parent.fileName.toString())
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.clipevery
package com.clipevery.platform

import com.clipevery.platform.currentPlatform
import kotlin.test.Test
import kotlin.test.assertTrue

Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ compose-material = { module = "androidx.compose.material:material", version.ref

[plugins]
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

0 comments on commit 7042d43

Please sign in to comment.