Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("convention.root-project")
alias(libs.plugins.kotlin.jvm) apply false
}

allprojects {
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ glide = "4.16.0"

koilVersion-compose = "2.2.0"

sdds-uikit = "0.14.0"
sdds-uikit-compose = "0.16.0"
sdds-uikit = "0.15.0"
sdds-uikit-compose = "0.17.0"
sdds-icons = "0.8.0"

plugin-androidCacheFix = "3.0.1"
Expand Down
2 changes: 1 addition & 1 deletion playground/sandbox-compose/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
versionMajor=0
versionMinor=9
versionMinor=10
versionPatch=0
11 changes: 11 additions & 0 deletions sdds-core/config-codec/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
id("convention.kotlin-java-version-sync")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}

dependencies {
implementation(libs.base.kotlin.serialization.json)
testImplementation(libs.base.test.unit.jUnit)
testImplementation(libs.base.test.unit.mockk)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.sdds.utils.config.codec

import com.sdds.utils.config.codec.internal.CommonConfig
import com.sdds.utils.config.codec.internal.ConfigCodec
import com.sdds.utils.config.codec.internal.NativeConfig
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File

private val serializer = Json {
ignoreUnknownKeys = true
}

fun main(args: Array<String>) {
if (args.size != 3) {
println("Usage: <input_json_path> <output_dir_path> <mode: encode|decode>")
return
}

val inputPath = args[0]
val outputPath = args[1]
val mode = args[2]

val inputFile = File(inputPath)
val outputFile = File(outputPath)

if (!inputFile.exists()) {
println("Input file does not exist: $inputPath")
return
}

val inputJson = inputFile.readText()

val outputJson = when (mode.lowercase()) {
"encode" -> ConfigCodec.encode(serializer.decodeFromString(inputJson)).let {
serializer.encodeToString<NativeConfig>(it)
}
"decode" -> ConfigCodec.decode(serializer.decodeFromString(inputJson)).let {
serializer.encodeToString<CommonConfig>(it)
}
else -> {
println("Invalid mode: $mode. Use 'encode' or 'decode'.")
return
}
}

outputFile.parentFile.mkdirs()
outputFile.writeText(outputJson)
println("Operation '$mode' completed. Output written to $outputPath")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sdds.utils.config.codec.internal

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject

@Serializable
internal data class CommonConfig(
val rootPropertyId: String = "",
val commonValues: JsonObject? = null,
val properties: List<ConfigProperty> = emptyList(),
)

@Serializable
internal data class ConfigProperty(
val id: String,
val name: String,
val values: Set<ConfigPropertyValue>
)

@Serializable
internal data class ConfigPropertyValue(
val name: String,
val targets: Set<ConfigPropertyTarget>? = null,
val props: JsonObject? = null,
)

@Serializable
internal data class ConfigPropertyTarget(
val properties: Set<ConfigPropertyTargetInfo>
)

@Serializable
internal data class ConfigPropertyTargetInfo(
val id: String,
val value: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package com.sdds.utils.config.codec.internal

internal object ConfigCodec {

fun encode(config: CommonConfig): NativeConfig {
val nativeConfig = NativeConfig(props = config.commonValues)

val viewProperty = config.properties.findLast { it.id == "view" }
val rootsViews = viewProperty?.values
?.filter { it.targets == null }
?.associate { it.name to NativeViewVariation(it.props) }
.orEmpty()

val targetRegistry = config.properties.flatMap { prop ->
prop.values.map { value ->
ConfigPropertyTargetInfo(prop.id, value.name)
}
}.groupBy { it.id }

val targetViewRegistry = viewProperty?.values
?.filter { it.targets != null }
?.flatMap { value ->
value.targets!!.map { value.copy(targets = setOf(it)) }
}
?.groupBy { it.targets!!.first() }
?.mapValues { entry ->
entry.value.associate { it.name to NativeViewVariation(it.props) }
}
?.mapKeys { entry ->
entry.key.properties.joinToString(".") { it.value }
}
.orEmpty()
.also { println("target views $it") }

val variations = config.properties.filter { it.id != "view" }
.flatMap { property ->
property.values
.flatMap { value ->
value.toNativeVariation(
property.id,
config.rootPropertyId,
targetRegistry,
targetViewRegistry
)
}
}.filter { it.props != null }


return nativeConfig.copy(view = rootsViews, variations = variations)
}

fun decode(config: NativeConfig): CommonConfig {
val variationRegistry = config.variations.associateBy { it.id }

val viewPropertyValues = config.view.map { (name, view) ->
ConfigPropertyValue(
name = name,
props = view.props,
targets = null
)
}

val targetViews = config.variations
.flatMap { variation ->
variation.view.map { (name, view) ->
var targetId = ""
val targets = variation.id.split(".")
.map { id ->
ConfigPropertyTargetInfo(
id = variationRegistry["$targetId$id"]?.kind.orEmpty(),
value = id
).also {
targetId = "$targetId$id."
}
}
.toSet()
.let { ConfigPropertyTarget(it) }
ConfigPropertyValue(
name = name,
props = view.props,
targets = setOf(targets)
)
}
}

val viewValues = (viewPropertyValues + targetViews).toSet()
val viewProperty = ConfigProperty(
id = "view",
name = "view",
values = viewValues
)
.takeIf { viewValues.isNotEmpty() }
.let { listOfNotNull(it) }
.mergeConfigProperty()

val variationPropertyValues = config.variations.map { variation ->
var targetId = ""
val targets = variation.id.split(".")
.dropLast(1)
.takeIf { it.isNotEmpty() }
?.map { id ->
ConfigPropertyTargetInfo(
id = variationRegistry["$targetId$id"]?.kind.orEmpty(),
value = id
).also { targetId = "$targetId$id." }
}
?.toSet()
?.let { setOf(ConfigPropertyTarget(it)) }

val value = ConfigPropertyValue(
name = variation.id.removePrefix("${variation.parent}."),
props = variation.props,
targets = targets
)
ConfigProperty(
id = variation.kind,
name = variation.kind,
values = setOf(value)
)
}.mergeConfigProperty()

return CommonConfig(
commonValues = config.props,
properties = viewProperty + variationPropertyValues,
rootPropertyId = "size"
)
}
}

private fun ConfigPropertyValue.toNativeVariation(
propertyId: String,
rootPropertyId: String,
targetRegistry: Map<String, List<ConfigPropertyTargetInfo>>,
targetViewRegistry: Map<String, Map<String, NativeViewVariation>> = emptyMap(),
): List<NativeVariation> {

if (targets != null) {
return targets.map { target ->
val parent = target.properties.joinToString(".") { it.value }
val id = "$parent.$name"
NativeVariation(
id = id,
kind = propertyId,
parent = parent,
props = props,
view = targetViewRegistry[id].orEmpty()
)
}
}
return targetRegistry[rootPropertyId]
?.takeIf { rootPropertyId != propertyId }
?.map { info ->
val parent = info.value
val id = "$parent.$name"
NativeVariation(
id = id,
kind = propertyId,
parent = parent,
props = props,
view = targetViewRegistry[id].orEmpty()
)
}
?: listOf(
NativeVariation(
id = name,
kind = propertyId,
props = props,
view = targetViewRegistry[name].orEmpty()
)
)
}

private fun List<ConfigProperty>.mergeConfigProperty(): List<ConfigProperty> {
return groupBy { it.id }.map { (id, group) ->
val name = group.first().name

val mergedValues = group.flatMap { it.values }
.groupBy { it.name to it.props }
.map { (key, valuesGroup) ->
val mergedTargets = valuesGroup
.mapNotNull { it.targets }
.flatten()
.toSet()
.takeIf { it.isNotEmpty() }

ConfigPropertyValue(
name = key.first,
props = key.second,
targets = mergedTargets
)
}.toSet()

ConfigProperty(
id = id,
name = name,
values = mergedValues
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sdds.utils.config.codec.internal

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject

@Serializable
internal data class NativeConfig(
var view: Map<String, NativeViewVariation> = emptyMap(),
var props: JsonObject? = null,
var variations: List<NativeVariation> = emptyList()
)

@Serializable
internal data class NativeViewVariation(
val props: JsonObject?
)

@Serializable
internal data class NativeVariation(
val id: String,
val kind: String,
val parent: String? = null,
val view: Map<String, NativeViewVariation> = emptyMap(),
val props: JsonObject? = null
)
Loading
Loading