Skip to content

Commit

Permalink
More stable implementation of Firestore encoding
Browse files Browse the repository at this point in the history
Make methods stricter and fail with Library exceptions so they can be caught consistently.
  • Loading branch information
Daeda88 committed Apr 11, 2024
1 parent 194e802 commit dc54714
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 238 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,18 @@ import kotlinx.serialization.descriptors.StructureKind
import java.lang.IllegalArgumentException
import kotlin.collections.set

actual data class EncodedObject(
val android: Map<String, Any?>
)
actual data class EncodedObject(actual val raw: Map<String, Any?>) {
actual companion object {
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
}
val android: Map<String, Any?> get() = raw
}

actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
@PublishedApi
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(toMap())

@PublishedApi
internal actual fun Map<*, *>.asEncodedObject() = EncodedObject(
map { (key, value) ->
if (key is String) {
key to value
} else {
throw IllegalArgumentException("Expected a String key but received $key")
}
}.toMap()
)
internal actual fun Any.asNativeMap(): Map<*, *>? = this as? Map<*, *>

actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
StructureKind.LIST -> mutableListOf<Any?>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ import kotlinx.serialization.encoding.CompositeEncoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.SerializersModule

expect class EncodedObject
expect class EncodedObject {
companion object {
val emptyEncodedObject: EncodedObject
}

expect val emptyEncodedObject: EncodedObject
val raw: Map<String, Any?>
}

@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(strategy, value) { encodeDefaults = shouldEncodeElementDefault }"))
fun <T> encode(strategy: SerializationStrategy<T>, value: T, shouldEncodeElementDefault: Boolean): Any? = encode(strategy, value) {
Expand All @@ -36,20 +40,12 @@ inline fun <reified T> encode(value: T, buildSettings: EncodeSettings.Builder.()
encode(value, EncodeSettings.BuilderImpl().apply(buildSettings).buildEncodeSettings())

inline fun <T : Any> encodeAsObject(strategy: SerializationStrategy<T>, value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
val encoded = encode(strategy, value, buildSettings)
return when (encoded) {
is Map<*, *> -> encoded.asEncodedObject()
null -> throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
else -> throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
}
val encoded = encode(strategy, value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
}
inline fun <reified T : Any> encodeAsObject(value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
val encoded = encode(value, buildSettings)
return when (encoded) {
is Map<*, *> -> encoded.asEncodedObject()
null -> throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
else -> throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
}
val encoded = encode(value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
}

@PublishedApi
Expand All @@ -67,7 +63,19 @@ internal inline fun <reified T> encode(value: T, encodeSettings: EncodeSettings)
}

@PublishedApi
internal expect fun Map<*, *>.asEncodedObject(): EncodedObject
expect internal fun Any.asNativeMap(): Map<*, *>?

@PublishedApi
internal fun Map<*, *>.asEncodedObject(): EncodedObject = map { (key, value) ->
if (key is String) {
key to value
} else {
throw IllegalArgumentException("Expected a String key but received $key")
}
}.asEncodedObject()

@PublishedApi
internal expect fun List<Pair<String, Any?>>.asEncodedObject(): EncodedObject

/**
* An extension which which serializer to use for value. Handy in updating fields by name or path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlin.collections.set

actual data class EncodedObject(
val ios: Map<Any?, *>
)
actual data class EncodedObject(actual val raw: Map<String, Any?>) {
actual companion object {
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
}

actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap<Any?, Any?>())
val ios: Map<Any?, *> get() = raw.mapKeys { (key, _) -> key as? Any }
}

@PublishedApi
internal actual fun Map<*, *>.asEncodedObject() = EncodedObject(
map { (key, value) ->
key to value
}.toMap()
)
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(toMap())

@PublishedApi
internal actual fun Any.asNativeMap(): Map<*, *>? = this as? Map<*, *>

actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
StructureKind.LIST -> encodeAsList()
StructureKind.MAP -> mutableListOf<Any?>()
Expand Down
31 changes: 20 additions & 11 deletions firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@ import kotlinx.serialization.descriptors.StructureKind
import kotlin.js.Json
import kotlin.js.json

actual typealias EncodedObject = Json
actual data class EncodedObject(private val keyValues: List<Pair<String, Any?>>) {
actual companion object {
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyList())
}

actual val raw get() = keyValues.toMap()
val json get() = json(*keyValues.toTypedArray())
}


actual val emptyEncodedObject: EncodedObject = json()
@PublishedApi
internal actual fun Map<*, *>.asEncodedObject() = json(
*map { (key, value) ->
if (key is String) {
key to value
} else {
throw IllegalArgumentException("Expected a String key but received $key")
}
}.toTypedArray()
)
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(this)

@PublishedApi
internal actual fun Any.asNativeMap(): Map<*, *>? = (this as? Json)?.let { json ->
val mutableMap = mutableMapOf<String, Any?>()
for (key in js("Object").keys(json)) {
mutableMap[key] = json[key]
}
mutableMap.toMap()
}

actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
StructureKind.LIST -> encodeAsList(descriptor)
StructureKind.MAP -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ internal actual class NativeDatabaseReference internal constructor(
}

actual suspend fun updateEncodedChildren(encodedUpdate: EncodedObject) =
rethrow { update(js, encodedUpdate).awaitWhileOnline(database) }
rethrow { update(js, encodedUpdate.json).awaitWhileOnline(database) }


actual suspend fun <T> runTransaction(strategy: KSerializer<T>, buildSettings: EncodeDecodeSettingsBuilder.() -> Unit, transactionUpdate: (currentData: T) -> T): DataSnapshot {
Expand Down Expand Up @@ -236,7 +236,7 @@ internal actual class NativeOnDisconnect internal constructor(
rethrow { js.set(encodedValue).awaitWhileOnline(database) }

actual suspend fun updateEncodedChildren(encodedUpdate: EncodedObject) =
rethrow { js.update(encodedUpdate).awaitWhileOnline(database) }
rethrow { js.update(encodedUpdate.json).awaitWhileOnline(database) }

}

Expand Down
Loading

0 comments on commit dc54714

Please sign in to comment.