Skip to content

Commit e7aa33f

Browse files
committed
WIP on custom decoder for jvm
1 parent 18db836 commit e7aa33f

File tree

21 files changed

+243
-117
lines changed

21 files changed

+243
-117
lines changed

build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
22

33
plugins {
4-
kotlin("multiplatform") version "1.3.50" apply false
5-
kotlin("plugin.serialization") version "1.3.50" apply false
4+
kotlin("multiplatform") version "1.3.60" apply false
65
}
76

87
buildscript {
@@ -32,6 +31,7 @@ subprojects {
3231
tasks.withType<KotlinCompile<*>> {
3332
kotlinOptions.freeCompilerArgs += listOf(
3433
"-Xuse-experimental=kotlin.Experimental",
34+
"-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
3535
"-Xuse-experimental=kotlinx.serialization.ImplicitReflectionSerializer"
3636
)
3737
}
@@ -46,7 +46,7 @@ subprojects {
4646
"androidMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.2")
4747
"jvmMainImplementation"(kotlin("stdlib-jdk8"))
4848
"jvmMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
49-
"jvmMainApi"("app.teamhub:firebase-java:0.2.0")
49+
"jvmMainApi"("app.teamhub:firebase-java:0.3.0")
5050
"jvmMainApi"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.2")
5151
}
5252
}

firebase-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"dependencies": {
2626
"@teamhubapp/firebase-common": "0.1.0",
2727
"firebase": "6.2.3",
28-
"kotlin": "1.3.50",
28+
"kotlin": "1.3.60",
2929
"kotlinx-coroutines-core": "1.2.2"
3030
}
3131
}

firebase-auth/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"dependencies": {
2626
"@teamhubapp/firebase-app": "0.1.0",
2727
"firebase": "6.2.3",
28-
"kotlin": "1.3.50",
28+
"kotlin": "1.3.60",
2929
"kotlinx-coroutines-core": "1.2.2"
3030
}
3131
}

firebase-common/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,19 @@ kotlin {
4747
val androidMain by getting {
4848
dependencies {
4949
api("com.google.firebase:firebase-common:19.2.0")
50+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
5051
}
5152
}
5253
val jsMain by getting {
5354
dependencies {
5455
// implementation(npm("firebase", "6.2.3"))
56+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.14.0")
5557
}
5658
}
5759
val jvmMain by getting {
60+
dependencies {
61+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
62+
}
5863
kotlin.srcDir("src/androidMain/kotlin")
5964
}
6065
}

firebase-common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"homepage": "https://github.com/TeamHubApp/firebase-kotlin-multiplatform-sdk",
2525
"dependencies": {
2626
"firebase": "6.2.3",
27-
"kotlin": "1.3.50",
27+
"kotlin": "1.3.60",
2828
"kotlinx-coroutines-core": "1.2.2"
2929
}
3030
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package dev.teamhub.firebase
2+
3+
import kotlinx.serialization.*
4+
import kotlinx.serialization.internal.*
5+
import kotlinx.serialization.modules.EmptyModule
6+
import kotlinx.serialization.modules.getContextualOrDefault
7+
import kotlin.reflect.KClass
8+
9+
object Mapper : AbstractSerialFormat(EmptyModule) {
10+
11+
internal class OutMapper : NamedValueEncoder() {
12+
13+
override fun beginCollection(
14+
desc: SerialDescriptor,
15+
collectionSize: Int,
16+
vararg typeParams: KSerializer<*>
17+
): CompositeEncoder {
18+
encodeTaggedInt(nested("size"), collectionSize)
19+
return this
20+
}
21+
22+
private var _map: MutableMap<String, Any> = mutableMapOf()
23+
24+
val map: Map<String, Any>
25+
get() = _map
26+
27+
override fun encodeTaggedValue(tag: String, value: Any) {
28+
_map[tag] = value
29+
}
30+
31+
override fun encodeTaggedNull(tag: String) {
32+
throw SerializationException("null is not supported. use Mapper.mapNullable()/OutNullableMapper instead")
33+
}
34+
}
35+
36+
internal class InMapper(private val map: Map<String, Any>) : NamedValueDecoder() {
37+
38+
override fun decodeCollectionSize(desc: SerialDescriptor): Int {
39+
return decodeTaggedInt(nested("size"))
40+
}
41+
42+
override fun decodeTaggedString(tag: String) = decodeTaggedValue(tag).toString()
43+
44+
override fun decodeTaggedDouble(tag: String) = when(val value = decodeTaggedValue(tag)) {
45+
is Number -> value.toDouble()
46+
is String -> value.toDouble()
47+
else -> super.decodeTaggedDouble(tag)
48+
}
49+
50+
override fun decodeTaggedLong(tag: String) = when(val value = decodeTaggedValue(tag)) {
51+
is Number -> value.toLong()
52+
is String -> value.toLong()
53+
else -> super.decodeTaggedLong(tag)
54+
}
55+
56+
override fun decodeTaggedByte(tag: String) = when(val value = decodeTaggedValue(tag)) {
57+
is Number -> value.toByte()
58+
is String -> value.toByte()
59+
else -> super.decodeTaggedByte(tag)
60+
}
61+
62+
override fun decodeTaggedFloat(tag: String) = when(val value = decodeTaggedValue(tag)) {
63+
is Number -> value.toFloat()
64+
is String -> value.toFloat()
65+
else -> super.decodeTaggedFloat(tag)
66+
}
67+
68+
override fun decodeTaggedInt(tag: String) = when(val value = decodeTaggedValue(tag)) {
69+
is Number -> value.toInt()
70+
is String -> value.toInt()
71+
else -> super.decodeTaggedInt(tag)
72+
}
73+
74+
override fun decodeTaggedShort(tag: String) = when(val value = decodeTaggedValue(tag)) {
75+
is Number -> value.toShort()
76+
is String -> value.toShort()
77+
else -> super.decodeTaggedShort(tag)
78+
}
79+
80+
override fun decodeTaggedValue(tag: String): Any = map.getValue(tag)
81+
}
82+
83+
fun <T> map(strategy: SerializationStrategy<T>, obj: T): Map<String, Any> {
84+
val m = OutMapper()
85+
m.encode(strategy, obj)
86+
return m.map
87+
}
88+
89+
@Suppress("UNCHECKED_CAST")
90+
fun <T> decode(strategy: DeserializationStrategy<T>, value: Any?): T = value?.let {
91+
when(strategy) {
92+
is BooleanSerializer -> value
93+
is StringSerializer -> value.toString()
94+
is IntSerializer -> when (value) {
95+
is Number -> value.toInt()
96+
is String -> value.toInt()
97+
else -> value
98+
}
99+
is ShortSerializer -> when (value) {
100+
is Number -> value.toShort()
101+
is String -> value.toShort()
102+
else -> value
103+
}
104+
is LongSerializer -> when (value) {
105+
is Number -> value.toLong()
106+
is String -> value.toLong()
107+
else -> value
108+
}
109+
is FloatSerializer -> when (value) {
110+
is Number -> value.toFloat()
111+
is String -> value.toFloat()
112+
else -> value
113+
}
114+
is ByteSerializer -> when (value) {
115+
is Number -> value.toByte()
116+
is String -> value.toByte()
117+
else -> value
118+
}
119+
is DoubleSerializer -> when (value) {
120+
is Number -> value.toDouble()
121+
is String -> value.toDouble()
122+
else -> value
123+
}
124+
is CharSerializer -> when (value) {
125+
is Number -> value.toChar()
126+
is String -> value.takeIf { it.length == 1 }?.let { it[0] } ?: value
127+
else -> value
128+
}
129+
else -> InMapper(value as Map<String, Any>).decode(strategy)
130+
}
131+
} as T
132+
133+
@ImplicitReflectionSerializer
134+
inline fun <reified T : Any> map(obj: T): Map<String, Any> = map(context.getContextualOrDefault(T::class), obj)
135+
136+
@ImplicitReflectionSerializer
137+
@Suppress("UNCHECKED_CAST")
138+
inline fun <reified T> decode(value: Any?): T = decode(context.getContextualOrDefault(T::class as KClass<*>) as DeserializationStrategy<T>, value)
139+
140+
}
141+

firebase-database/build.gradle.kts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import org.apache.tools.ant.taskdefs.condition.Os
33
plugins {
44
id("com.android.library")
55
kotlin("multiplatform")
6-
kotlin("plugin.serialization")
76
`maven-publish`
87
}
98

@@ -46,24 +45,24 @@ kotlin {
4645
dependencies {
4746
api(project(":firebase-app"))
4847
implementation(project(":firebase-common"))
49-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.13.0")
48+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0")
5049
}
5150
}
5251
val androidMain by getting {
5352
dependencies {
5453
api("com.google.firebase:firebase-database:17.0.0")
55-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0")
54+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
5655
}
5756
}
5857
val jvmMain by getting {
5958
dependencies {
60-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0")
59+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
6160
}
6261
kotlin.srcDir("src/androidMain/kotlin")
6362
}
6463
val jsMain by getting {
6564
dependencies {
66-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.13.0")
65+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.14.0")
6766
}
6867
}
6968
}

firebase-database/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"dependencies": {
2626
"@teamhubapp/firebase-app": "0.1.0",
2727
"firebase": "6.2.3",
28-
"kotlin": "1.3.50",
28+
"kotlin": "1.3.60",
2929
"kotlinx-coroutines-core": "1.2.2"
3030
}
3131
}

firebase-database/src/androidMain/kotlin/dev/teamhub/firebase/database/database.kt

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import com.google.firebase.database.ServerValue
55
import com.google.firebase.database.ValueEventListener
66
import dev.teamhub.firebase.Firebase
77
import dev.teamhub.firebase.FirebaseApp
8+
import dev.teamhub.firebase.Mapper
89
import kotlinx.coroutines.channels.awaitClose
910
import kotlinx.coroutines.flow.callbackFlow
1011
import kotlinx.coroutines.tasks.await
11-
import kotlinx.serialization.*
12+
import kotlinx.serialization.DeserializationStrategy
13+
import kotlinx.serialization.SerializationStrategy
14+
import kotlinx.serialization.serializer
1215

1316
actual val Firebase.database
1417
get() = FirebaseDatabase(com.google.firebase.database.FirebaseDatabase.getInstance())
@@ -31,9 +34,6 @@ actual class FirebaseDatabase internal constructor(val android: com.google.fireb
3134
android.setLogLevel(Logger.Level.DEBUG.takeIf { enabled } ?: Logger.Level.NONE)
3235
}
3336

34-
@Serializable
35-
data class Value<T>(val value: T)
36-
3737
actual class DatabaseReference internal constructor(val android: com.google.firebase.database.DatabaseReference) {
3838

3939
actual fun push() = DatabaseReference(android.push())
@@ -54,34 +54,27 @@ actual class DatabaseReference internal constructor(val android: com.google.fire
5454
}
5555

5656
actual suspend inline fun <reified T : Any> setValue(value: T) =
57-
android.setValue(Mapper.map(Value(value))["value"]).await().run { Unit }
57+
android.setValue(Mapper.map(T::class.serializer(), value)).await().run { Unit }
5858

59-
actual suspend inline fun <reified T> setValue(value: T, strategy: SerializationStrategy<T>) =
60-
object : KSerializer<T>, SerializationStrategy<T> by strategy {
61-
override fun deserialize(decoder: Decoder) = error("not supported")
62-
}.let {
63-
android.setValue(Mapper.map(Value.serializer(it), Value(value))["value"]).await()
64-
}.run { Unit }
59+
actual suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T) =
60+
android.setValue(Mapper.map(strategy, value)).await().run { Unit }
6561

6662
actual suspend fun updateChildren(update: Map<String, Any?>) =
67-
android.updateChildren(Mapper.map(update)).await().run { Unit }
63+
android.updateChildren(update.mapValues { Mapper.map(it) }).await().run { Unit }
6864

6965
actual suspend fun removeValue() = android.removeValue().await().run { Unit }
7066
}
7167

7268
@Suppress("UNCHECKED_CAST")
7369
actual class DataSnapshot internal constructor(val android: com.google.firebase.database.DataSnapshot) {
70+
7471
actual val exists get() = android.exists()
7572

76-
actual inline fun <reified T: Any> value() =
77-
Mapper.unmapNullable<Value<T>>(mapOf("value" to android.value)).value
73+
actual inline fun <reified T> value() =
74+
Mapper.decode<T>(android.value)
7875

7976
actual inline fun <reified T> value(strategy: DeserializationStrategy<T>) =
80-
object : KSerializer<T>, DeserializationStrategy<T> by strategy {
81-
override fun serialize(encoder: Encoder, obj: T) = error("not supported")
82-
}.let {
83-
Mapper.unmapNullable(Value.serializer(it), mapOf("value" to android.value)).value
84-
}
77+
Mapper.decode(strategy,android.value)
8578

8679
actual fun child(path: String) = DataSnapshot(android.child(path))
8780
actual val children: Iterable<DataSnapshot> get() = android.children.map { DataSnapshot(it) }
@@ -93,17 +86,13 @@ actual class OnDisconnect internal constructor(val android: com.google.firebase.
9386
actual suspend fun cancel() = android.cancel().await().run { Unit }
9487

9588
actual suspend inline fun <reified T : Any> setValue(value: T) =
96-
android.setValue(Mapper.map(Value(value))["value"]).await().run { Unit }
89+
android.setValue(Mapper.map(value)).await().run { Unit }
9790

98-
actual suspend inline fun <reified T> setValue(value: T, strategy: SerializationStrategy<T>) =
99-
object : KSerializer<T>, SerializationStrategy<T> by strategy {
100-
override fun deserialize(decoder: Decoder) = error("not supported")
101-
}.let {
102-
android.setValue(Mapper.map(Value.serializer(it), Value(value))["value"]).await()
103-
}.run { Unit }
91+
actual suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T) =
92+
android.setValue(Mapper.map(strategy, value)).await().run { Unit }
10493

10594
actual suspend fun updateChildren(update: Map<String, Any?>) =
106-
android.updateChildren(Mapper.map(update)).await().run { Unit }
95+
android.updateChildren(update.mapValues { Mapper.map(it) }).await().run { Unit }
10796
}
10897

10998
actual typealias DatabaseException = com.google.firebase.database.DatabaseException

firebase-database/src/commonMain/kotlin/dev/teamhub/firebase/database/database.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ expect class DatabaseReference {
3131
val snapshots: Flow<DataSnapshot>
3232
@ImplicitReflectionSerializer
3333
suspend inline fun <reified T: Any> setValue(value: T)
34-
suspend inline fun <reified T> setValue(value: T, strategy: SerializationStrategy<T>)
34+
suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T)
3535
@ImplicitReflectionSerializer
3636
suspend fun updateChildren(update: Map<String, Any?>)
3737
suspend fun removeValue()
@@ -40,7 +40,7 @@ expect class DatabaseReference {
4040
expect class DataSnapshot {
4141
val exists: Boolean
4242
@ImplicitReflectionSerializer
43-
inline fun <reified T: Any> value(): T
43+
inline fun <reified T> value(): T
4444
inline fun <reified T> value(strategy: DeserializationStrategy<T>): T
4545
fun child(path: String): DataSnapshot
4646
val children: Iterable<DataSnapshot>
@@ -57,7 +57,7 @@ expect class OnDisconnect {
5757
suspend fun cancel()
5858
@ImplicitReflectionSerializer
5959
suspend inline fun <reified T: Any> setValue(value: T)
60-
suspend inline fun <reified T> setValue(value: T, strategy: SerializationStrategy<T>)
60+
suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T)
6161
@ImplicitReflectionSerializer
6262
suspend fun updateChildren(update: Map<String, Any?>)
6363
}

0 commit comments

Comments
 (0)