-
-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Here's my take on the implementation of this pattern from the lexicons which makes for nice and usable Kotlin types while still serializing/deserializing correctly.
Essentially, these two classes exist already, hand-written, in Butterfly somewhere (currently in Types.kt).
@Serializable
abstract class Union<T> {
abstract val value: T
}
open class StringUnionSerializer<U: Union<String>>(
private val from: (String) -> U,
): KSerializer<U> {
override val descriptor: SerialDescriptor = String.serializer().descriptor
override fun deserialize(decoder: Decoder): U {
return from(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: U) {
encoder.encodeString(value.value)
}
}And then for lexicon JSON like this, snipped from com.atproto.label.defs.json:
"labelValue": {
"type": "string",
"knownValues": [
"!hide",
"!no-promote",
"!warn",
"!no-unauthenticated",
"dmca-violation",
"doxxing",
"porn",
"sexual",
"nudity",
"nsfl",
"gore"
]
}We generate the following Kotlin code.
object LabelValueSerializer: StringUnionSerializer<LabelValue>({ LabelValue.from(it) })
@Serializable(with = LabelValueSerializer::class)
sealed class LabelValue(override val value: String): Union<String>() {
companion object {
inline fun <reified U: LabelValue> from(value: String): U = when(value) {
Warn.value -> Warn as U
NoPromote.value -> NoPromote as U
NoSelf.value -> NoSelf as U
NoUnauthenticated.value -> NoUnauthenticated as U
Hide.value -> Hide as U
DMCAViolation.value -> DMCAViolation as U
Doxxing.value -> Doxxing as U
Porn.value -> Porn as U
Sexual.value -> Sexual as U
Nudity.value -> Nudity as U
NSFL.value -> NSFL as U
Gore.value -> Gore as U
GraphicMedia.value -> GraphicMedia as U
else -> Custom(value) as U
}
}
data object Warn: LabelValue("!warn")
data object NoPromote: LabelValue("!no-promote")
data object NoUnauthenticated: LabelValue("!no-unauthenticated")
data object NoSelf: LabelValue("!no-self")
data object Hide: LabelValue("!hide")
data object DMCAViolation: LabelValue("dmca-violation")
data object Doxxing: LabelValue("doxxing")
data object Porn: LabelValue("porn")
data object Sexual: LabelValue("sexual")
data object Nudity: LabelValue("nudity")
data object NSFL: LabelValue("nsfl")
data object Gore: LabelValue("gore")
data object GraphicMedia: LabelValue("graphic-media")
data class Custom(override val value: String): LabelValue(value)
override fun toString(): String = value
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LabelValue) return false
if (value != other.value) return false
return true
}
override fun hashCode(): Int {
return value.hashCode()
}
}Metadata
Metadata
Assignees
Labels
No labels