Skip to content

Commit 9aab7b8

Browse files
committed
completed custom decoder for jvm
1 parent e7aa33f commit 9aab7b8

File tree

7 files changed

+269
-228
lines changed
  • firebase-common/src/androidMain/kotlin/dev/teamhub/firebase
  • firebase-database/src/androidMain/kotlin/dev/teamhub/firebase/database
  • firebase-firestore/src
  • firebase-functions/src/androidMain/kotlin/dev/teamhub/firebase/functions

7 files changed

+269
-228
lines changed

firebase-common/src/androidMain/kotlin/dev/teamhub/firebase/MapEncoder.kt

Lines changed: 0 additions & 141 deletions
This file was deleted.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package dev.teamhub.firebase
2+
3+
import kotlinx.serialization.*
4+
import kotlinx.serialization.CompositeDecoder.Companion.READ_ALL
5+
import kotlinx.serialization.CompositeDecoder.Companion.READ_DONE
6+
import kotlinx.serialization.internal.nullable
7+
import kotlinx.serialization.modules.EmptyModule
8+
import kotlinx.serialization.modules.SerialModule
9+
import kotlinx.serialization.modules.getContextualOrDefault
10+
import kotlin.reflect.KClass
11+
12+
@Suppress("UNCHECKED_CAST")
13+
inline fun <reified T> encode(strategy: SerializationStrategy<T>? = null /*value?.let { EmptyModule.getContextualOrDefault(it::class as KClass<*>) } as SerializationStrategy<T>*/, value: T) =
14+
value as Any?
15+
16+
@Suppress("UNCHECKED_CAST")
17+
inline fun <reified T> decode(strategy: DeserializationStrategy<T> = EmptyModule.getContextualOrDefault(T::class as KClass<Any>).run { if(null is T) nullable else this } as DeserializationStrategy<T>, value: Any?): T {
18+
require(value != null || strategy.descriptor.isNullable) { "Value was null for non-nullable type ${T::class}" }
19+
return FirebaseDecoder(value).decode(strategy)
20+
}
21+
22+
class FirebaseDecoder(private val value: Any?) : Decoder {
23+
24+
override val context: SerialModule
25+
get() = EmptyModule
26+
27+
override val updateMode: UpdateMode = UpdateMode.BANNED
28+
29+
@Suppress("UNCHECKED_CAST")
30+
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>) = when(desc.kind as StructureKind) {
31+
StructureKind.CLASS -> FirebaseClassDecoder(value as Map<String, Any?>)
32+
StructureKind.LIST -> FirebaseCompositeDecoder(value as List<*>)
33+
StructureKind.MAP -> (value as Map<*, *>).let { map ->
34+
FirebaseCompositeDecoder(map.flatMap { listOf(it.key, it.value) }, map.size)
35+
}
36+
}
37+
38+
override fun decodeString() = decodeString(value)
39+
40+
override fun decodeDouble() = decodeDouble(value)
41+
42+
override fun decodeLong() = decodeLong(value)
43+
44+
override fun decodeByte() = decodeByte(value)
45+
46+
override fun decodeFloat() = decodeFloat(value)
47+
48+
override fun decodeInt() = decodeInt(value)
49+
50+
override fun decodeShort() = decodeShort(value)
51+
52+
override fun decodeBoolean() = decodeBoolean(value)
53+
54+
override fun decodeChar() = decodeChar(value)
55+
56+
override fun decodeEnum(enumDescription: SerialDescriptor) = decodeEnum(value, enumDescription)
57+
58+
override fun decodeNotNullMark() = decodeNotNullMark(value)
59+
60+
override fun decodeNull() = decodeNull(value)
61+
62+
override fun decodeUnit() = decodeUnit(value)
63+
64+
}
65+
66+
class FirebaseClassDecoder(private val map: Map<String, Any?>) : FirebaseCompositeDecoder(
67+
map.size, { desc, index -> map[desc.getElementName(index)] }
68+
) {
69+
private var index: Int = 0
70+
71+
override fun decodeElementIndex(desc: SerialDescriptor): Int =
72+
(index until desc.elementsCount)
73+
.firstOrNull { !desc.isElementOptional(it) || map.containsKey(desc.getElementName(it)) }
74+
?.also { index = it + 1 }
75+
?: READ_DONE
76+
}
77+
78+
open class FirebaseCompositeDecoder(
79+
private val size: Int,
80+
private val get: (desc: SerialDescriptor, index: Int) -> Any?
81+
): CompositeDecoder {
82+
83+
constructor(list: List<Any?>, size: Int = list.size): this(size, { _, index -> list[index] })
84+
85+
override val context = EmptyModule
86+
override val updateMode = UpdateMode.BANNED
87+
88+
override fun decodeElementIndex(desc: SerialDescriptor) = READ_ALL
89+
90+
override fun decodeCollectionSize(desc: SerialDescriptor) = size
91+
92+
override fun <T> decodeSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T =
93+
deserializer.deserialize(FirebaseDecoder(get(desc, index)))
94+
95+
override fun <T : Any> decodeNullableSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>): T? =
96+
if(decodeNotNullMark(get(desc, index))) decodeSerializableElement(desc, index, deserializer) else decodeNull(get(desc, index))
97+
98+
override fun <T> updateSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>, old: T): T =
99+
throw UpdateNotSupportedException(deserializer.descriptor.name)
100+
101+
override fun <T : Any> updateNullableSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>, old: T?): T? =
102+
throw UpdateNotSupportedException(deserializer.descriptor.name)
103+
104+
override fun decodeBooleanElement(desc: SerialDescriptor, index: Int) = decodeBoolean(get(desc, index))
105+
106+
override fun decodeByteElement(desc: SerialDescriptor, index: Int) = decodeByte(get(desc, index))
107+
108+
override fun decodeCharElement(desc: SerialDescriptor, index: Int) = decodeChar(get(desc, index))
109+
110+
override fun decodeDoubleElement(desc: SerialDescriptor, index: Int) = decodeDouble(get(desc, index))
111+
112+
override fun decodeFloatElement(desc: SerialDescriptor, index: Int) = decodeFloat(get(desc, index))
113+
114+
override fun decodeIntElement(desc: SerialDescriptor, index: Int) = decodeInt(get(desc, index))
115+
116+
override fun decodeLongElement(desc: SerialDescriptor, index: Int) = decodeLong(get(desc, index))
117+
118+
override fun decodeShortElement(desc: SerialDescriptor, index: Int) = decodeShort(get(desc, index))
119+
120+
override fun decodeStringElement(desc: SerialDescriptor, index: Int) = decodeString(get(desc, index))
121+
122+
override fun decodeUnitElement(desc: SerialDescriptor, index: Int) = decodeUnit(get(desc, index))
123+
124+
}
125+
126+
private fun decodeString(value: Any?) = value.toString()
127+
128+
private fun decodeDouble(value: Any?) = when(value) {
129+
is Number -> value.toDouble()
130+
is String -> value.toDouble()
131+
else -> throw SerializationException("Expected $value to be double")
132+
}
133+
134+
private fun decodeLong(value: Any?) = when(value) {
135+
is Number -> value.toLong()
136+
is String -> value.toLong()
137+
else -> throw SerializationException("Expected $value to be long")
138+
}
139+
140+
private fun decodeByte(value: Any?) = when(value) {
141+
is Number -> value.toByte()
142+
is String -> value.toByte()
143+
else -> throw SerializationException("Expected $value to be byte")
144+
}
145+
146+
private fun decodeFloat(value: Any?) = when(value) {
147+
is Number -> value.toFloat()
148+
is String -> value.toFloat()
149+
else -> throw SerializationException("Expected $value to be float")
150+
}
151+
152+
private fun decodeInt(value: Any?) = when(value) {
153+
is Number -> value.toInt()
154+
is String -> value.toInt()
155+
else -> throw SerializationException("Expected $value to be int")
156+
}
157+
158+
private fun decodeShort(value: Any?) = when(value) {
159+
is Number -> value.toShort()
160+
is String -> value.toShort()
161+
else -> throw SerializationException("Expected $value to be short")
162+
}
163+
164+
private fun decodeBoolean(value: Any?) = value as Boolean
165+
166+
private fun decodeChar(value: Any?) = when(value) {
167+
is Number -> value.toChar()
168+
is String -> value[0]
169+
else -> throw SerializationException("Expected $value to be char")
170+
}
171+
172+
private fun decodeEnum(value: Any?, enumDescription: SerialDescriptor) = when(value) {
173+
is Number -> value.toInt()
174+
is String -> enumDescription.getElementIndexOrThrow(value)
175+
else -> throw SerializationException("Expected $value to be enum")
176+
}
177+
178+
private fun decodeNotNullMark(value: Any?) = value != null
179+
180+
private fun decodeNull(value: Any?) = value as Nothing?
181+
182+
private fun decodeUnit(value: Any?) = value as Unit
183+

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ 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
8+
import dev.teamhub.firebase.decode
9+
import dev.teamhub.firebase.encode
910
import kotlinx.coroutines.channels.awaitClose
1011
import kotlinx.coroutines.flow.callbackFlow
1112
import kotlinx.coroutines.tasks.await
@@ -54,13 +55,13 @@ actual class DatabaseReference internal constructor(val android: com.google.fire
5455
}
5556

5657
actual suspend inline fun <reified T : Any> setValue(value: T) =
57-
android.setValue(Mapper.map(T::class.serializer(), value)).await().run { Unit }
58+
android.setValue(encode(T::class.serializer(), value)).await().run { Unit }
5859

5960
actual suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T) =
60-
android.setValue(Mapper.map(strategy, value)).await().run { Unit }
61+
android.setValue(encode(strategy, value)).await().run { Unit }
6162

6263
actual suspend fun updateChildren(update: Map<String, Any?>) =
63-
android.updateChildren(update.mapValues { Mapper.map(it) }).await().run { Unit }
64+
android.updateChildren(update.mapValues { (_, it) -> encode(value = it) }).await().run { Unit }
6465

6566
actual suspend fun removeValue() = android.removeValue().await().run { Unit }
6667
}
@@ -71,10 +72,10 @@ actual class DataSnapshot internal constructor(val android: com.google.firebase.
7172
actual val exists get() = android.exists()
7273

7374
actual inline fun <reified T> value() =
74-
Mapper.decode<T>(android.value)
75+
decode<T>(value = android.value)
7576

7677
actual inline fun <reified T> value(strategy: DeserializationStrategy<T>) =
77-
Mapper.decode(strategy,android.value)
78+
decode(strategy, android.value)
7879

7980
actual fun child(path: String) = DataSnapshot(android.child(path))
8081
actual val children: Iterable<DataSnapshot> get() = android.children.map { DataSnapshot(it) }
@@ -86,13 +87,13 @@ actual class OnDisconnect internal constructor(val android: com.google.firebase.
8687
actual suspend fun cancel() = android.cancel().await().run { Unit }
8788

8889
actual suspend inline fun <reified T : Any> setValue(value: T) =
89-
android.setValue(Mapper.map(value)).await().run { Unit }
90+
android.setValue(encode(value = value)).await().run { Unit }
9091

9192
actual suspend inline fun <reified T> setValue(strategy: SerializationStrategy<T>, value: T) =
92-
android.setValue(Mapper.map(strategy, value)).await().run { Unit }
93+
android.setValue(encode(strategy, value)).await().run { Unit }
9394

9495
actual suspend fun updateChildren(update: Map<String, Any?>) =
95-
android.updateChildren(update.mapValues { Mapper.map(it) }).await().run { Unit }
96+
android.updateChildren(update.mapValues { (_, it) -> encode(value = it) }).await().run { Unit }
9697
}
9798

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

0 commit comments

Comments
 (0)