commonMain.dev.gitlive.firebase.internal.encoders.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of firebase-common-internal-jvm Show documentation
Show all versions of firebase-common-internal-jvm Show documentation
The Firebase Kotlin SDK is a Kotlin-first SDK for Firebase. It's API is similar to the Firebase Android SDK Kotlin Extensions but also supports multiplatform projects, enabling you to use Firebase directly from your common source targeting iOS, Android or JS.
/*
* Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license.
*/
package dev.gitlive.firebase.internal
import dev.gitlive.firebase.EncodeSettings
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.CompositeEncoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.SerializersModule
@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(strategy, value) { encodeDefaults = shouldEncodeElementDefault }"))
public fun encode(strategy: SerializationStrategy, value: T, shouldEncodeElementDefault: Boolean): Any? = encode(strategy, value) {
this.encodeDefaults = shouldEncodeElementDefault
}
public inline fun encode(strategy: SerializationStrategy, value: T, buildSettings: EncodeSettings.Builder.() -> Unit): Any? =
encode(strategy, value, EncodeSettingsImpl.Builder().apply(buildSettings).buildEncodeSettings())
@PublishedApi
internal fun encode(strategy: SerializationStrategy, value: T, encodeSettings: EncodeSettings): Any? =
FirebaseEncoder(encodeSettings).apply { encodeSerializableValue(strategy, value) }.value
@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(value) { this.encodeDefaults = shouldEncodeElementDefault }"))
public inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = encode(value) {
this.encodeDefaults = shouldEncodeElementDefault
}
public inline fun encode(value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Any? =
encode(value, EncodeSettingsImpl.Builder().apply(buildSettings).buildEncodeSettings())
/**
* Encodes data as an [EncodedObject].
* This is not recommended for manual use, but may be done by the library internally.
* @throws IllegalArgumentException if [value] is not valid as an [EncodedObject] (e.g. not encodable in the form Map
*/
public inline fun encodeAsObject(strategy: SerializationStrategy, value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
if (value is Map<*, *> && value.keys.any { it !is String }) {
throw IllegalArgumentException("$value is a Map containing non-String keys. Must be of the form Map")
}
val encoded = encode(strategy, value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map")
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map")
}
/**
* Encodes data as an [EncodedObject].
* This is not recommended for manual use, but may be done by the library internally.
* @throws IllegalArgumentException if [value] is not valid as an [EncodedObject] (e.g. not encodable in the form Map
*/
public inline fun encodeAsObject(value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
if (value is Map<*, *> && value.keys.any { it !is String }) {
throw IllegalArgumentException("$value is a Map containing non-String keys. Must be of the form Map")
}
val encoded = encode(value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map")
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map")
}
@PublishedApi
internal inline fun encode(value: T, encodeSettings: EncodeSettings): Any? = value?.let {
FirebaseEncoder(encodeSettings).apply {
if (it is ValueWithSerializer<*> && it.value is T) {
@Suppress("UNCHECKED_CAST")
(it as ValueWithSerializer).let {
encodeSerializableValue(it.serializer, it.value)
}
} else {
encodeSerializableValue(it.firebaseSerializer(), it)
}
}.value
}
/**
* An extension which which serializer to use for value. Handy in updating fields by name or path
* where using annotation is not possible
* @return a value with a custom serializer.
*/
public fun T.withSerializer(serializer: SerializationStrategy): Any = ValueWithSerializer(this, serializer)
public data class ValueWithSerializer(val value: T, val serializer: SerializationStrategy)
public expect fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder
public class FirebaseEncoder(
internal val settings: EncodeSettings,
) : Encoder {
public constructor(shouldEncodeElementDefault: Boolean) : this(
EncodeSettingsImpl.Builder().apply { this.encodeDefaults = shouldEncodeElementDefault }.buildEncodeSettings(),
)
public var value: Any? = null
override val serializersModule: SerializersModule = settings.serializersModule
private var polymorphicDiscriminator: String? = null
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
val encoder = structureEncoder(descriptor)
if (polymorphicDiscriminator != null) {
encoder.encodePolymorphicClassDiscriminator(polymorphicDiscriminator!!, descriptor.serialName)
polymorphicDiscriminator = null
}
return encoder
}
override fun encodeBoolean(value: Boolean) {
this.value = value
}
override fun encodeByte(value: Byte) {
this.value = value
}
override fun encodeChar(value: Char) {
this.value = value
}
override fun encodeDouble(value: Double) {
this.value = value
}
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
this.value = enumDescriptor.getElementName(index)
}
override fun encodeFloat(value: Float) {
this.value = value
}
override fun encodeInt(value: Int) {
this.value = value
}
override fun encodeLong(value: Long) {
this.value = value
}
override fun encodeNotNullMark() {
// no-op
}
override fun encodeNull() {
this.value = null
}
override fun encodeShort(value: Short) {
this.value = value
}
override fun encodeString(value: String) {
this.value = value
}
override fun encodeInline(descriptor: SerialDescriptor): Encoder = this
override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) {
encodePolymorphically(serializer, value) {
polymorphicDiscriminator = it
}
}
}
public open class FirebaseCompositeEncoder(
private val settings: EncodeSettings,
private val end: () -> Unit = {},
private val setPolymorphicType: (String, String) -> Unit = { _, _ -> },
private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit,
) : CompositeEncoder {
// private fun SerializationStrategy.toFirebase(): SerializationStrategy = when(descriptor.kind) {
// StructureKind.MAP -> FirebaseMapSerializer(descriptor.getElementDescriptor(1)) as SerializationStrategy
// StructureKind.LIST -> FirebaseListSerializer(descriptor.getElementDescriptor(0)) as SerializationStrategy
// else -> this
// }
override val serializersModule: SerializersModule = settings.serializersModule
override fun endStructure(descriptor: SerialDescriptor): Unit = end()
override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = settings.encodeDefaults
override fun encodeNullableSerializableElement(
descriptor: SerialDescriptor,
index: Int,
serializer: SerializationStrategy,
value: T?,
): Unit = set(
descriptor,
index,
value?.let {
FirebaseEncoder(settings).apply {
encodeSerializableValue(serializer, value)
}.value
},
)
override fun encodeSerializableElement(
descriptor: SerialDescriptor,
index: Int,
serializer: SerializationStrategy,
value: T,
): Unit = set(
descriptor,
index,
FirebaseEncoder(settings).apply {
encodeSerializableValue(serializer, value)
}.value,
)
public fun encodeObject(descriptor: SerialDescriptor, index: Int, value: T): Unit = set(descriptor, index, value)
override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean): Unit = set(descriptor, index, value)
override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte): Unit = set(descriptor, index, value)
override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char): Unit = set(descriptor, index, value)
override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double): Unit = set(descriptor, index, value)
override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float): Unit = set(descriptor, index, value)
override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int): Unit = set(descriptor, index, value)
override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long): Unit = set(descriptor, index, value)
override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short): Unit = set(descriptor, index, value)
override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String): Unit = set(descriptor, index, value)
@ExperimentalSerializationApi
override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder =
FirebaseEncoder(settings)
public fun encodePolymorphicClassDiscriminator(discriminator: String, type: String) {
setPolymorphicType(discriminator, type)
}
}