[KxSerialization] Cover all combinations of language and serialization features with tests for enums
Resolves https://github.com/Kotlin/kotlinx.serialization/issues/2324 Merge-request: KT-MR-13775 Merged-by: Sergei Shanshin <Sergey.Shanshin@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
e39af4583e
commit
9a8ebd5c21
@@ -0,0 +1,254 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
//
|
||||
// NOTE: THIS FILE IS AUTO-GENERATED by the TestMatrixIntegration.kt, DO NOT EDIT!
|
||||
//
|
||||
|
||||
@file:UseSerializers(EnumWithUseSerializer::class, NestedEnumWithUseSerializer::class, )
|
||||
@file:UseContextualSerialization(EnumWithUseContextual::class, Container.NestedEnumWithUseContextual::class, )
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.modules.*
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
enum class EnumWithDef {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class Enum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable(EnumWithCustomSerializer::class)
|
||||
enum class EnumWithCustom {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable(EnumWithCustomClSerializer::class)
|
||||
enum class EnumWithCustomCl {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class EnumWithContextual {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class EnumWithUseContextual {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class EnumWithUse {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Extra("Enum1")
|
||||
enum class Enum1 {
|
||||
@Extra("A") A,
|
||||
@Extra("B") B,
|
||||
}
|
||||
|
||||
class Container {
|
||||
enum class NestedEnumWithDef {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class NestedEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable(NestedEnumWithCustomSerializer::class)
|
||||
enum class NestedEnumWithCustom {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
@Serializable(NestedEnumWithCustomClSerializer::class)
|
||||
enum class NestedEnumWithCustomCl {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class NestedEnumWithContextual {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class NestedEnumWithUseContextual {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
enum class NestedEnumWithUse {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Outer {
|
||||
}
|
||||
|
||||
object EnumWithCustomSerializer: ToDoSerializer<EnumWithCustom>("custom|EnumWithCustom")
|
||||
object NestedEnumWithCustomSerializer: ToDoSerializer<Container.NestedEnumWithCustom>("custom|Container.NestedEnumWithCustom")
|
||||
class EnumWithCustomClSerializer: ToDoSerializer<EnumWithCustomCl>("custom|EnumWithCustomCl")
|
||||
class NestedEnumWithCustomClSerializer: ToDoSerializer<Container.NestedEnumWithCustomCl>("custom|Container.NestedEnumWithCustomCl")
|
||||
|
||||
object EnumWithContextualSerializer: ToDoSerializer<EnumWithContextual>("contextual|EnumWithContextual")
|
||||
object NestedEnumWithContextualSerializer: ToDoSerializer<Container.NestedEnumWithContextual>("contextual|Container.NestedEnumWithContextual")
|
||||
object EnumWithUseContextualSerializer: ToDoSerializer<EnumWithUseContextual>("contextual|EnumWithUseContextual")
|
||||
object NestedEnumWithUseContextualSerializer: ToDoSerializer<Container.NestedEnumWithUseContextual>("contextual|Container.NestedEnumWithUseContextual")
|
||||
|
||||
class EnumWithUseSerializer: ToDoSerializer<EnumWithUse>("useSerializer|EnumWithUse")
|
||||
class NestedEnumWithUseSerializer: ToDoSerializer<Container.NestedEnumWithUse>("useSerializer|Container.NestedEnumWithUse")
|
||||
|
||||
@kotlinx.serialization.SerialInfo
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||
annotation class Extra(val value: String)
|
||||
|
||||
fun box(): String {
|
||||
val module = SerializersModule {
|
||||
contextual(EnumWithContextualSerializer)
|
||||
contextual(NestedEnumWithContextualSerializer)
|
||||
contextual(EnumWithUseContextualSerializer)
|
||||
contextual(NestedEnumWithUseContextualSerializer)
|
||||
}
|
||||
|
||||
serializer<EnumWithDef>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithDef>().checkElements("A", "B")
|
||||
serializer<Enum>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnum>().checkElements("A", "B")
|
||||
serializer<EnumWithCustom>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithCustom>().checkElements("A", "B")
|
||||
serializer<EnumWithCustomCl>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithCustomCl>().checkElements("A", "B")
|
||||
serializer<EnumWithContextual>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithContextual>().checkElements("A", "B")
|
||||
serializer<EnumWithUseContextual>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithUseContextual>().checkElements("A", "B")
|
||||
serializer<EnumWithUse>().checkElements("A", "B")
|
||||
serializer<Container.NestedEnumWithUse>().checkElements("A", "B")
|
||||
serializer<Enum1>().checkElements("A", "B")
|
||||
|
||||
// Call serializer factory function in companion
|
||||
Enum.serializer().checkSerialName("Enum")?.let { return it }
|
||||
Container.NestedEnum.serializer().checkSerialName("Container.NestedEnum")?.let { return it }
|
||||
EnumWithCustom.serializer().checkSerialName("custom|EnumWithCustom")?.let { return it }
|
||||
Container.NestedEnumWithCustom.serializer().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
|
||||
EnumWithCustomCl.serializer().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
|
||||
Container.NestedEnumWithCustomCl.serializer().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
|
||||
Enum1.serializer().checkSerialName("Enum1")?.let { return it }
|
||||
|
||||
// Serializer lookup by generic parameter
|
||||
serializer<EnumWithDef>().checkSerialName("EnumWithDef")?.let { return it }
|
||||
serializer<Container.NestedEnumWithDef>().checkSerialName("Container.NestedEnumWithDef")?.let { return it }
|
||||
serializer<Enum>().checkSerialName("Enum")?.let { return it }
|
||||
serializer<Container.NestedEnum>().checkSerialName("Container.NestedEnum")?.let { return it }
|
||||
serializer<EnumWithCustom>().checkSerialName("custom|EnumWithCustom")?.let { return it }
|
||||
serializer<Container.NestedEnumWithCustom>().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
|
||||
serializer<EnumWithCustomCl>().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
|
||||
serializer<Container.NestedEnumWithCustomCl>().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer<EnumWithUseContextual>().checkSerialName("EnumWithUseContextual")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer<Container.NestedEnumWithUseContextual>().checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer<EnumWithUse>().checkSerialName("EnumWithUse")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer<Container.NestedEnumWithUse>().checkSerialName("Container.NestedEnumWithUse")?.let { return it }
|
||||
serializer<Enum1>().checkSerialName("Enum1")?.let { return it }
|
||||
|
||||
// Serializer lookup by typeOf function
|
||||
serializer(typeOf<EnumWithDef>()).checkSerialName("EnumWithDef")?.let { return it }
|
||||
serializer(typeOf<Container.NestedEnumWithDef>()).checkSerialName("Container.NestedEnumWithDef")?.let { return it }
|
||||
serializer(typeOf<Enum>()).checkSerialName("Enum")?.let { return it }
|
||||
serializer(typeOf<Container.NestedEnum>()).checkSerialName("Container.NestedEnum")?.let { return it }
|
||||
serializer(typeOf<EnumWithCustom>()).checkSerialName("custom|EnumWithCustom")?.let { return it }
|
||||
serializer(typeOf<Container.NestedEnumWithCustom>()).checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
|
||||
serializer(typeOf<EnumWithCustomCl>()).checkSerialName("custom|EnumWithCustomCl")?.let { return it }
|
||||
serializer(typeOf<Container.NestedEnumWithCustomCl>()).checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer(typeOf<EnumWithUseContextual>()).checkSerialName("EnumWithUseContextual")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer(typeOf<Container.NestedEnumWithUseContextual>()).checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer(typeOf<EnumWithUse>()).checkSerialName("EnumWithUse")?.let { return it }
|
||||
// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
serializer(typeOf<Container.NestedEnumWithUse>()).checkSerialName("Container.NestedEnumWithUse")?.let { return it }
|
||||
serializer(typeOf<Enum1>()).checkSerialName("Enum1")?.let { return it }
|
||||
|
||||
// Serializer lookup by generic parameter in custom module
|
||||
module.serializer<EnumWithDef>().checkSerialName("EnumWithDef")?.let { return it }
|
||||
module.serializer<Container.NestedEnumWithDef>().checkSerialName("Container.NestedEnumWithDef")?.let { return it }
|
||||
module.serializer<Enum>().checkSerialName("Enum")?.let { return it }
|
||||
module.serializer<Container.NestedEnum>().checkSerialName("Container.NestedEnum")?.let { return it }
|
||||
module.serializer<EnumWithCustom>().checkSerialName("custom|EnumWithCustom")?.let { return it }
|
||||
module.serializer<Container.NestedEnumWithCustom>().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
|
||||
module.serializer<EnumWithCustomCl>().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
|
||||
module.serializer<Container.NestedEnumWithCustomCl>().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer<EnumWithUseContextual>().checkSerialName("EnumWithUseContextual")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer<Container.NestedEnumWithUseContextual>().checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer<EnumWithUse>().checkSerialName("EnumWithUse")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer<Container.NestedEnumWithUse>().checkSerialName("Container.NestedEnumWithUse")?.let { return it }
|
||||
module.serializer<Enum1>().checkSerialName("Enum1")?.let { return it }
|
||||
|
||||
// Serializer lookup by typeOf function in custom module
|
||||
module.serializer(typeOf<EnumWithDef>()).checkSerialName("EnumWithDef")?.let { return it }
|
||||
module.serializer(typeOf<Container.NestedEnumWithDef>()).checkSerialName("Container.NestedEnumWithDef")?.let { return it }
|
||||
module.serializer(typeOf<Enum>()).checkSerialName("Enum")?.let { return it }
|
||||
module.serializer(typeOf<Container.NestedEnum>()).checkSerialName("Container.NestedEnum")?.let { return it }
|
||||
module.serializer(typeOf<EnumWithCustom>()).checkSerialName("custom|EnumWithCustom")?.let { return it }
|
||||
module.serializer(typeOf<Container.NestedEnumWithCustom>()).checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
|
||||
module.serializer(typeOf<EnumWithCustomCl>()).checkSerialName("custom|EnumWithCustomCl")?.let { return it }
|
||||
module.serializer(typeOf<Container.NestedEnumWithCustomCl>()).checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer(typeOf<EnumWithUseContextual>()).checkSerialName("EnumWithUseContextual")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer(typeOf<Container.NestedEnumWithUseContextual>()).checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer(typeOf<EnumWithUse>()).checkSerialName("EnumWithUse")?.let { return it }
|
||||
// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
|
||||
module.serializer(typeOf<Container.NestedEnumWithUse>()).checkSerialName("Container.NestedEnumWithUse")?.let { return it }
|
||||
module.serializer(typeOf<Enum1>()).checkSerialName("Enum1")?.let { return it }
|
||||
|
||||
// Annotation on type should have value same as a class name
|
||||
serializer<Enum1>().checkAnnotation("Enum1")
|
||||
|
||||
// Annotation on enum entries should have value same as a entry names
|
||||
serializer<Enum1>().checkElementAnnotations("A", "B")
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun KSerializer<*>.checkSerialName(name: String): String? = if (descriptor.serialName != name) "Wrong serial name: Expected '$name' actual '${descriptor.serialName}'" else null
|
||||
fun KSerializer<*>.checkAnnotation(value: String): String? = descriptor.annotations.filterIsInstance<Extra>().single().value.let { if (it != value) it else null }
|
||||
|
||||
fun KSerializer<*>.checkElementAnnotations(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementAnnotations(it).filterIsInstance<Extra>().single().value}.let { if (it != values.toList()) it.toString() else null }
|
||||
|
||||
fun KSerializer<*>.checkElements(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementName(it) }.let { if (it != values.toList()) it.toString() else null }
|
||||
|
||||
|
||||
abstract class ToDoSerializer<T: Any>(descriptorName: String): KSerializer<T> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
|
||||
override fun deserialize(decoder: Decoder): T = TODO()
|
||||
override fun serialize(encoder: Encoder, value: T) = TODO()
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlinx.serialization.TestGeneratorKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("plugins/kotlinx-serialization/testData/matrix")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class FirTestMatrixGenerated extends AbstractFirTestMatrix {
|
||||
@Test
|
||||
public void testAllFilesPresentInMatrix() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/matrix"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enums.kt")
|
||||
public void testEnums() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/matrix/enums.kt");
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlinx.serialization.TestGeneratorKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("plugins/kotlinx-serialization/testData/matrix")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TestMatrixGenerated extends AbstractTestMatrix {
|
||||
@Test
|
||||
public void testAllFilesPresentInMatrix() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/matrix"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enums.kt")
|
||||
public void testEnums() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/matrix/enums.kt");
|
||||
}
|
||||
}
|
||||
+6
@@ -7,6 +7,8 @@ package org.jetbrains.kotlinx.serialization
|
||||
|
||||
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
|
||||
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
|
||||
import org.jetbrains.kotlinx.serialization.matrix.cases.enumsTestMatrix
|
||||
import org.jetbrains.kotlinx.serialization.matrix.testMatrix
|
||||
import org.jetbrains.kotlinx.serialization.runners.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -77,6 +79,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractSerializationFirJsBoxTest> {
|
||||
model("boxIr")
|
||||
}
|
||||
|
||||
testMatrix {
|
||||
add("enums") { enumsTestMatrix() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix
|
||||
|
||||
|
||||
interface CombinationContext {
|
||||
fun defineEnums(
|
||||
serializers: Set<SerializerKind>,
|
||||
locations: Set<TypeLocation>,
|
||||
optionsConfig: EnumOptionsBuilder.() -> Unit = {},
|
||||
): Set<EnumVariant>
|
||||
|
||||
fun function(name: String, block: FunctionContext.() -> Unit)
|
||||
|
||||
fun box(block: FunctionContext.() -> Unit)
|
||||
|
||||
fun generate(appendable: Appendable, generator: String)
|
||||
|
||||
val TypeVariant.named: NamedTypeVariant
|
||||
}
|
||||
|
||||
|
||||
interface FunctionContext {
|
||||
fun line(code: String = "")
|
||||
}
|
||||
|
||||
interface EnumOptionsBuilder {
|
||||
fun serialInfo(vararg serialInfo: SerialInfo)
|
||||
fun descriptorAccessing(vararg descriptorAccessing: DescriptorAccessing)
|
||||
|
||||
fun entries(vararg entries: String)
|
||||
}
|
||||
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.SerializerKind.BY_DEFAULT
|
||||
|
||||
internal fun hasFactoryFun(type: TypeVariant): Boolean {
|
||||
return type.features.serializer in setOf(
|
||||
SerializerKind.GENERATED,
|
||||
SerializerKind.CUSTOM_OBJECT,
|
||||
SerializerKind.CUSTOM_CLASS
|
||||
)
|
||||
}
|
||||
|
||||
internal fun canBeUsedInLookup(type: TypeVariant): Boolean {
|
||||
return type.features.serializer in setOf(
|
||||
SerializerKind.GENERATED,
|
||||
SerializerKind.CUSTOM_OBJECT,
|
||||
SerializerKind.CUSTOM_CLASS,
|
||||
SerializerKind.CLASS_USE_SERIALIZER,
|
||||
SerializerKind.USE_CONTEXTUAL
|
||||
) || (type is EnumVariant && type.features.serializer == BY_DEFAULT)
|
||||
}
|
||||
internal fun canBeUsedInModuleLookup(type: TypeVariant): Boolean {
|
||||
return type.features.serializer in setOf(
|
||||
SerializerKind.GENERATED,
|
||||
SerializerKind.CUSTOM_OBJECT,
|
||||
SerializerKind.CUSTOM_CLASS,
|
||||
SerializerKind.CLASS_USE_SERIALIZER,
|
||||
SerializerKind.USE_CONTEXTUAL
|
||||
) || (type is EnumVariant && type.features.serializer == BY_DEFAULT)
|
||||
}
|
||||
|
||||
internal fun shouldAddContextualSerializerToModule(type: TypeVariant): Boolean {
|
||||
return type.features.serializer == SerializerKind.CONTEXTUAL || type.features.serializer == SerializerKind.USE_CONTEXTUAL
|
||||
}
|
||||
|
||||
internal fun hasAnnotationOnType(type: TypeVariant): Boolean {
|
||||
return type is EnumVariant && SerialInfo.ON_TYPE in type.options.serialInfo
|
||||
}
|
||||
|
||||
internal fun hasAnnotationOnElement(type: TypeVariant): Boolean {
|
||||
return type is EnumVariant && SerialInfo.ON_ELEMENTS in type.options.serialInfo
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix
|
||||
|
||||
import org.jetbrains.kotlin.generators.TestGroup
|
||||
import org.jetbrains.kotlin.incremental.deleteDirectoryContents
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.runners.codegen.AbstractFirLightTreeBlackBoxCodegenTest
|
||||
import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest
|
||||
import org.jetbrains.kotlinx.serialization.matrix.impl.CombinationContextImpl
|
||||
import org.jetbrains.kotlinx.serialization.configureForKotlinxSerialization
|
||||
import java.io.File
|
||||
|
||||
internal fun TestGroup.testMatrix(casesBlock: TestCaseContext.() -> Unit) {
|
||||
val relativeRootPath = "matrix"
|
||||
val dir = File("$testDataRoot/$relativeRootPath")
|
||||
dir.mkdirs()
|
||||
dir.deleteDirectoryContents()
|
||||
|
||||
val caseContext = TestCaseContext()
|
||||
caseContext.casesBlock()
|
||||
|
||||
caseContext.testCases.forEach { (caseName, block) ->
|
||||
val combinationContext = CombinationContextImpl()
|
||||
combinationContext.block()
|
||||
combinationContext.generateInto(dir.resolve("$caseName.kt"))
|
||||
}
|
||||
|
||||
testClass<AbstractTestMatrix> {
|
||||
model(relativeRootPath)
|
||||
}
|
||||
testClass<AbstractFirTestMatrix> {
|
||||
model(relativeRootPath)
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestCaseContext {
|
||||
val testCases: MutableMap<String, CombinationContext.() -> Unit> = mutableMapOf()
|
||||
|
||||
fun add(name: String, block: CombinationContext.() -> Unit) {
|
||||
testCases[name] = block
|
||||
}
|
||||
}
|
||||
|
||||
internal open class AbstractTestMatrix : AbstractIrBlackBoxCodegenTest() {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
builder.configureForKotlinxSerialization()
|
||||
}
|
||||
}
|
||||
|
||||
internal open class AbstractFirTestMatrix : AbstractFirLightTreeBlackBoxCodegenTest() {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
builder.configureForKotlinxSerialization()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CombinationContext.generateInto(file: File) {
|
||||
file.writer().use {
|
||||
generate(it, "TestMatrixIntegration.kt")
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.impl.CLASS_FOR_NESTED
|
||||
|
||||
|
||||
internal fun Set<TypeVariant>.filterEnums(predicate: (EnumVariant) -> Boolean): Set<EnumVariant> {
|
||||
val filtered = filterIsInstance<EnumVariant>().filter { variant -> predicate(variant) }
|
||||
return filtered.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* A combination of mutually exclusive Kotlin language and serialization features.
|
||||
*/
|
||||
sealed class TypeFeatures {
|
||||
abstract val serializer: SerializerKind
|
||||
abstract val location: TypeLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of the type used to generate type definitions and their uses.
|
||||
*
|
||||
* Parent type.
|
||||
*/
|
||||
sealed class TypeVariant {
|
||||
/**
|
||||
* A combination of mutually exclusive Kotlin language and serialization features.
|
||||
*/
|
||||
abstract val features: TypeFeatures
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of the enum types used to generate type definitions and their uses.
|
||||
*/
|
||||
data class EnumVariant(
|
||||
override val features: EnumFeatures,
|
||||
val options: EnumOptions,
|
||||
) : TypeVariant()
|
||||
|
||||
/**
|
||||
* A combination of mutually exclusive Kotlin language and serialization features for enum types.
|
||||
*/
|
||||
data class EnumFeatures(
|
||||
override val serializer: SerializerKind,
|
||||
override val location: TypeLocation,
|
||||
) : TypeFeatures()
|
||||
|
||||
/**
|
||||
* Optional features for the type.
|
||||
*/
|
||||
data class EnumOptions(
|
||||
val serialInfo: Set<SerialInfo>,
|
||||
val descriptorAccessing: Set<DescriptorAccessing>,
|
||||
val entries: Set<String>,
|
||||
)
|
||||
|
||||
|
||||
enum class SerializerKind {
|
||||
/**
|
||||
* Serializable by default (Without @Serializable annotation: enum, interface, sealed interface)
|
||||
*/
|
||||
BY_DEFAULT,
|
||||
|
||||
/**
|
||||
* @Serializable
|
||||
*/
|
||||
GENERATED,
|
||||
|
||||
/**
|
||||
* @Serializable(CustomObjectSerializer::class)
|
||||
*/
|
||||
CUSTOM_OBJECT,
|
||||
|
||||
/**
|
||||
* @Serializable(CustomSerializer::class)
|
||||
*/
|
||||
CUSTOM_CLASS,
|
||||
|
||||
/**
|
||||
* Contextual by SerialModule.
|
||||
*/
|
||||
CONTEXTUAL,
|
||||
|
||||
/**
|
||||
* Classes marked by file-level annotation @file:UseContextualSerialization
|
||||
*/
|
||||
USE_CONTEXTUAL,
|
||||
|
||||
/**
|
||||
* Serializable by @UseSerializers
|
||||
*/
|
||||
CLASS_USE_SERIALIZER
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of the type definition.
|
||||
*/
|
||||
enum class TypeLocation {
|
||||
FILE_ROOT,
|
||||
LOCAL,
|
||||
NESTED,
|
||||
}
|
||||
|
||||
enum class SerialInfo {
|
||||
ON_TYPE,
|
||||
ON_ELEMENTS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kotlin and serialization feature that are not mutually exclusive and there may be several features of the same kind.
|
||||
*/
|
||||
interface TypeOptionalFeature
|
||||
|
||||
enum class DescriptorAccessing : TypeOptionalFeature {
|
||||
FROM_INIT,
|
||||
FROM_COMPANION_INIT,
|
||||
FROM_COMPANION_PROPERTY_INIT
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A pair in the description of the type and the name that was assigned to it during generation.
|
||||
*/
|
||||
class NamedTypeVariant(val name: String, val variant: TypeVariant) {
|
||||
internal val classUsage: String
|
||||
get() {
|
||||
return when (variant.features.location) {
|
||||
TypeLocation.FILE_ROOT -> name
|
||||
TypeLocation.LOCAL -> name
|
||||
TypeLocation.NESTED -> "$CLASS_FOR_NESTED.$name"
|
||||
}
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix.cases
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.SerializerKind.*
|
||||
import org.jetbrains.kotlinx.serialization.matrix.TypeLocation.FILE_ROOT
|
||||
import org.jetbrains.kotlinx.serialization.matrix.TypeLocation.NESTED
|
||||
import org.jetbrains.kotlinx.serialization.matrix.*
|
||||
import org.jetbrains.kotlinx.serialization.matrix.impl.*
|
||||
import org.jetbrains.kotlinx.serialization.matrix.impl.elements
|
||||
import org.jetbrains.kotlinx.serialization.matrix.impl.serialName
|
||||
|
||||
fun CombinationContext.enumsTestMatrix() {
|
||||
val enums = defineEnums(
|
||||
SerializerKind.entries.toSet(),
|
||||
setOf(FILE_ROOT, NESTED)
|
||||
) {
|
||||
entries("A", "B")
|
||||
descriptorAccessing(*DescriptorAccessing.entries.toTypedArray())
|
||||
}
|
||||
|
||||
val enumsWithAnnotations = defineEnums(
|
||||
setOf(GENERATED),
|
||||
setOf(FILE_ROOT)
|
||||
) {
|
||||
entries("A", "B")
|
||||
serialInfo(*SerialInfo.entries.toTypedArray())
|
||||
}
|
||||
|
||||
val allTypes = enums + enumsWithAnnotations
|
||||
|
||||
val withCompanion = allTypes.filter(::hasFactoryFun)
|
||||
val lookupTypes = allTypes.filter(::canBeUsedInLookup)
|
||||
|
||||
val moduleLookupTypes = allTypes.filter(::canBeUsedInModuleLookup)
|
||||
|
||||
val annotatedTypes = allTypes.filter(::hasAnnotationOnType)
|
||||
val annotatedElementsTypes = allTypes.filter(::hasAnnotationOnElement)
|
||||
|
||||
box() {
|
||||
line("val module = SerializersModule {")
|
||||
allTypes.filter(::shouldAddContextualSerializerToModule).forEach { type ->
|
||||
line(" contextual(${type.named.serializerName})")
|
||||
}
|
||||
line("}")
|
||||
line()
|
||||
|
||||
allTypes.forEach { type ->
|
||||
line("serializer<${type.named.classUsage}>().checkElements(${
|
||||
type.elements.joinToString(", ") { "\"$it\"" }
|
||||
})")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Call serializer factory function in companion")
|
||||
withCompanion.forEach { type ->
|
||||
line("${type.named.classUsage}.serializer().checkSerialName(\"${type.named.serialName}\")?.let { return it }")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Serializer lookup by generic parameter")
|
||||
lookupTypes.forEach { type ->
|
||||
val serialName = if (type.features.serializer == CLASS_USE_SERIALIZER || type.features.serializer == USE_CONTEXTUAL) {
|
||||
line("// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers")
|
||||
type.named.classUsage
|
||||
} else {
|
||||
type.named.serialName
|
||||
}
|
||||
line("serializer<${type.named.classUsage}>().checkSerialName(\"$serialName\")?.let { return it }")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Serializer lookup by typeOf function")
|
||||
lookupTypes.forEach { type ->
|
||||
val serialName = if (type.features.serializer == CLASS_USE_SERIALIZER || type.features.serializer == USE_CONTEXTUAL) {
|
||||
line("// generated serializer used in lookup for the empty module in case of specifying of @file:UseContextualSerialization or @UseSerializers")
|
||||
type.named.classUsage
|
||||
} else {
|
||||
type.named.serialName
|
||||
}
|
||||
line("serializer(typeOf<${type.named.classUsage}>()).checkSerialName(\"$serialName\")?.let { return it }")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Serializer lookup by generic parameter in custom module")
|
||||
moduleLookupTypes.forEach { type ->
|
||||
val serialName = if (type.features.serializer == CLASS_USE_SERIALIZER || type.features.serializer == USE_CONTEXTUAL) {
|
||||
line("// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers")
|
||||
type.named.classUsage
|
||||
} else {
|
||||
type.named.serialName
|
||||
}
|
||||
line("module.serializer<${type.named.classUsage}>().checkSerialName(\"$serialName\")?.let { return it }")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Serializer lookup by typeOf function in custom module")
|
||||
moduleLookupTypes.forEach { type ->
|
||||
val serialName = if (type.features.serializer == CLASS_USE_SERIALIZER || type.features.serializer == USE_CONTEXTUAL) {
|
||||
line("// !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers")
|
||||
type.named.classUsage
|
||||
} else {
|
||||
type.named.serialName
|
||||
}
|
||||
line("module.serializer(typeOf<${type.named.classUsage}>()).checkSerialName(\"$serialName\")?.let { return it }")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Annotation on type should have value same as a class name")
|
||||
annotatedTypes.forEach { type ->
|
||||
line("serializer<${type.named.classUsage}>().checkAnnotation(\"${type.named.classUsage}\")")
|
||||
}
|
||||
line()
|
||||
|
||||
line("// Annotation on enum entries should have value same as a entry names")
|
||||
annotatedElementsTypes.forEach { type ->
|
||||
line("serializer<${type.named.classUsage}>().checkElementAnnotations(${
|
||||
type.elements.joinToString(", ") { "\"$it\"" }
|
||||
})")
|
||||
}
|
||||
line()
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix.impl
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.*
|
||||
|
||||
internal class CombinationContextImpl : CombinationContext {
|
||||
private val typesByFeatures = mutableMapOf<TypeFeatures, MutableList<TypeVariant>>()
|
||||
private val types: MutableMap<TypeVariant, String> = mutableMapOf()
|
||||
private val functions: MutableMap<String, FunctionContext.() -> Unit> = mutableMapOf()
|
||||
|
||||
override fun defineEnums(
|
||||
serializers: Set<SerializerKind>,
|
||||
locations: Set<TypeLocation>,
|
||||
optionsConfig: EnumOptionsBuilder.() -> Unit,
|
||||
): Set<EnumVariant> {
|
||||
val enumTypes: MutableSet<EnumVariant> = mutableSetOf()
|
||||
val options = EnumOptionsBuilderImpl().also { it.optionsConfig() }.build()
|
||||
for (serializer in serializers) {
|
||||
for (location in locations) {
|
||||
val features = EnumFeatures(serializer, location)
|
||||
enumTypes += EnumVariant(features, options)
|
||||
}
|
||||
}
|
||||
addTypes(enumTypes)
|
||||
return enumTypes
|
||||
}
|
||||
|
||||
override fun function(name: String, block: FunctionContext.() -> Unit) {
|
||||
functions[name] = block
|
||||
}
|
||||
|
||||
override fun box(block: FunctionContext.() -> Unit) {
|
||||
functions["box(): String"] = {
|
||||
block()
|
||||
line("return \"OK\"")
|
||||
}
|
||||
}
|
||||
|
||||
override fun generate(appendable: Appendable, generator: String) {
|
||||
val namedTypes = types.map { (type, name) -> NamedTypeVariant(name, type) }
|
||||
appendable.writeHeader(namedTypes, generator)
|
||||
appendable.writeTypes(namedTypes)
|
||||
|
||||
functions.forEach { (signature, builder) ->
|
||||
appendable.writeFunction(signature, builder)
|
||||
}
|
||||
|
||||
appendable.writeUtils()
|
||||
}
|
||||
|
||||
override val TypeVariant.named: NamedTypeVariant
|
||||
get() = NamedTypeVariant(types[this] ?: throw Exception("Type variant wasn't defined properly $this"), this)
|
||||
|
||||
private fun addTypes(types: Iterable<TypeVariant>) {
|
||||
for (type in types) {
|
||||
val typesForFeatures = typesByFeatures.getOrPut(type.features) { mutableListOf() }
|
||||
val className = type.className + if (typesForFeatures.size > 0) typesForFeatures.size.toString() else ""
|
||||
typesForFeatures += type
|
||||
this.types[type] = className
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EnumOptionsBuilderImpl : EnumOptionsBuilder {
|
||||
private val serialInfoP: MutableSet<SerialInfo> = mutableSetOf()
|
||||
private val descriptorAccessingP: MutableSet<DescriptorAccessing> = mutableSetOf()
|
||||
private val entriesP: MutableSet<String> = mutableSetOf()
|
||||
|
||||
|
||||
override fun serialInfo(vararg serialInfo: SerialInfo) {
|
||||
serialInfo.forEach { serialInfoP.add(it) }
|
||||
}
|
||||
|
||||
override fun descriptorAccessing(vararg descriptorAccessing: DescriptorAccessing) {
|
||||
descriptorAccessing.forEach { descriptorAccessingP.add(it) }
|
||||
}
|
||||
|
||||
override fun entries(vararg entries: String) {
|
||||
entries.forEach { entriesP.add(it) }
|
||||
}
|
||||
|
||||
fun build(): EnumOptions {
|
||||
return EnumOptions(serialInfoP, descriptorAccessingP, entriesP)
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix.impl
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.FunctionContext
|
||||
|
||||
internal fun Appendable.writeFunction(signature: String, builder: FunctionContext.() -> Unit) {
|
||||
appendLine("fun $signature {")
|
||||
|
||||
val context = object : FunctionContext {
|
||||
override fun line(code: String) {
|
||||
append(" ")
|
||||
appendLine(code)
|
||||
}
|
||||
}
|
||||
|
||||
context.builder()
|
||||
|
||||
appendLine("}")
|
||||
appendLine()
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix.impl
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.EnumVariant
|
||||
import org.jetbrains.kotlinx.serialization.matrix.NamedTypeVariant
|
||||
import org.jetbrains.kotlinx.serialization.matrix.SerialInfo
|
||||
import org.jetbrains.kotlinx.serialization.matrix.SerializerKind.*
|
||||
import org.jetbrains.kotlinx.serialization.matrix.TypeLocation.*
|
||||
|
||||
/*
|
||||
|
||||
<file level declarations>
|
||||
|
||||
class Container {
|
||||
<nested declarations>
|
||||
}
|
||||
|
||||
class Outer {
|
||||
<inner declarations>
|
||||
}
|
||||
|
||||
fun <function name> () {
|
||||
<local classes>
|
||||
<function declaration>
|
||||
}
|
||||
|
||||
*/
|
||||
internal fun Appendable.writeTypes(types: List<NamedTypeVariant>) {
|
||||
// file-level
|
||||
types.filter { type -> type.variant.features.location == FILE_ROOT }.forEach { type ->
|
||||
writeTypeDef(type)
|
||||
}
|
||||
|
||||
// nested
|
||||
append("class ")
|
||||
append(CLASS_FOR_NESTED)
|
||||
append(" {\n")
|
||||
types.filter { named -> named.variant.features.location == NESTED }.forEach { type ->
|
||||
writeTypeDef(type, " ")
|
||||
}
|
||||
append("}\n\n")
|
||||
|
||||
// write inner
|
||||
append("class ")
|
||||
append(CLASS_FOR_INNER)
|
||||
append(" {\n")
|
||||
types.filter { named -> named.variant.features.location == LOCAL }.forEach { type ->
|
||||
writeTypeDef(type, " ")
|
||||
}
|
||||
append("}\n\n")
|
||||
|
||||
// write custom serializers
|
||||
types.forEach { type ->
|
||||
writeCustomSerializer(type)
|
||||
}
|
||||
|
||||
appendLine()
|
||||
|
||||
// write contextual serializers
|
||||
types.forEach { type ->
|
||||
writeContextualSerializer(type)
|
||||
}
|
||||
|
||||
appendLine()
|
||||
|
||||
// write use serializers
|
||||
types.forEach { type ->
|
||||
writeUseSerializer(type)
|
||||
}
|
||||
|
||||
appendLine()
|
||||
|
||||
writeSerialInfo()
|
||||
|
||||
appendLine()
|
||||
}
|
||||
|
||||
internal fun Appendable.writeHeader(types: List<NamedTypeVariant>, generator: String) {
|
||||
appendLine("// WITH_STDLIB")
|
||||
appendLine()
|
||||
appendLine("//")
|
||||
appendLine("// NOTE: THIS FILE IS AUTO-GENERATED by the $generator, DO NOT EDIT!")
|
||||
appendLine("//")
|
||||
appendLine()
|
||||
writeUseSerializers(types)
|
||||
writeUseContextualSerializers(types)
|
||||
appendLine()
|
||||
appendLine("import kotlinx.serialization.*")
|
||||
appendLine("import kotlinx.serialization.descriptors.*")
|
||||
appendLine("import kotlinx.serialization.json.*")
|
||||
appendLine("import kotlinx.serialization.encoding.*")
|
||||
appendLine("import kotlinx.serialization.modules.*")
|
||||
appendLine("import kotlin.reflect.typeOf")
|
||||
appendLine()
|
||||
}
|
||||
|
||||
internal fun Appendable.writeUtils() {
|
||||
append("fun KSerializer<*>.checkSerialName(name: String): String? = if (descriptor.serialName != name) \"Wrong serial name: Expected '\$name' actual '\${descriptor.serialName}'\" else null\n")
|
||||
append("fun KSerializer<*>.checkAnnotation(value: String): String? = descriptor.annotations.filterIsInstance<Extra>().single().value.let { if (it != value) it else null }\n\n")
|
||||
append("fun KSerializer<*>.checkElementAnnotations(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementAnnotations(it).filterIsInstance<Extra>().single().value}.let { if (it != values.toList()) it.toString() else null }\n\n")
|
||||
append("fun KSerializer<*>.checkElements(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementName(it) }.let { if (it != values.toList()) it.toString() else null }\n\n")
|
||||
|
||||
// write abstract serializer
|
||||
appendLine(TODO_SERIALIZER)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private fun Appendable.writeTypeDef(named: NamedTypeVariant, indent: String = "") {
|
||||
when (named.variant) {
|
||||
is EnumVariant -> writeEnumDef(named, indent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Appendable.writeEnumDef(named: NamedTypeVariant, indent: String) {
|
||||
val enum = named.variant as EnumVariant
|
||||
|
||||
if (enum.features.location == LOCAL) {
|
||||
throw IllegalArgumentException("Local enums are not allowed, can't generate test code.")
|
||||
}
|
||||
|
||||
val classUsage = named.classUsage
|
||||
|
||||
if (enum.features.serializer == GENERATED) {
|
||||
append(indent)
|
||||
appendLine("@Serializable")
|
||||
}
|
||||
if (enum.features.serializer == CUSTOM_OBJECT || enum.features.serializer == CUSTOM_CLASS) {
|
||||
append(indent)
|
||||
appendLine("@Serializable(${named.serializerName}::class)")
|
||||
}
|
||||
|
||||
if (SerialInfo.ON_TYPE in enum.options.serialInfo) {
|
||||
append(indent)
|
||||
appendLine("@$SERIAL_ANNOTATION(\"$classUsage\")")
|
||||
}
|
||||
|
||||
append(indent)
|
||||
appendLine("enum class ${named.name} {")
|
||||
enum.options.entries.forEach { entry ->
|
||||
append(indent)
|
||||
append(" ")
|
||||
|
||||
if (SerialInfo.ON_ELEMENTS in enum.options.serialInfo) {
|
||||
append("@$SERIAL_ANNOTATION(\"$entry\") ")
|
||||
}
|
||||
append(entry)
|
||||
appendLine(",")
|
||||
}
|
||||
|
||||
append(indent)
|
||||
appendLine("}")
|
||||
appendLine()
|
||||
}
|
||||
|
||||
|
||||
private fun Appendable.writeUseSerializers(types: List<NamedTypeVariant>) {
|
||||
val serializers = types.mapNotNull { type -> type.useSerializer }
|
||||
if (serializers.isEmpty()) {
|
||||
return
|
||||
}
|
||||
append("@file:UseSerializers(")
|
||||
serializers.forEach { name ->
|
||||
append(name)
|
||||
append("::class, ")
|
||||
}
|
||||
appendLine(")")
|
||||
}
|
||||
|
||||
private fun Appendable.writeUseContextualSerializers(types: List<NamedTypeVariant>) {
|
||||
val types = types.filter { type -> type.useContextualSerializer != null }
|
||||
if (types.isEmpty()) {
|
||||
return
|
||||
}
|
||||
append("@file:UseContextualSerialization(")
|
||||
types.forEach { type ->
|
||||
append(type.classUsage)
|
||||
append("::class, ")
|
||||
}
|
||||
appendLine(")")
|
||||
}
|
||||
|
||||
private fun Appendable.writeSerialInfo() {
|
||||
appendLine("@kotlinx.serialization.SerialInfo")
|
||||
appendLine("@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)")
|
||||
appendLine("annotation class $SERIAL_ANNOTATION(val value: String)")
|
||||
}
|
||||
|
||||
private fun Appendable.writeCustomSerializer(type: NamedTypeVariant) {
|
||||
when (type.variant.features.serializer) {
|
||||
CUSTOM_CLASS -> {
|
||||
appendLine("class ${type.serializerName}: ToDoSerializer<${type.classUsage}>(\"${type.serialName}\")")
|
||||
}
|
||||
CUSTOM_OBJECT -> {
|
||||
appendLine("object ${type.serializerName}: ToDoSerializer<${type.classUsage}>(\"${type.serialName}\")")
|
||||
}
|
||||
else -> Unit // no-op
|
||||
}
|
||||
}
|
||||
|
||||
private fun Appendable.writeContextualSerializer(type: NamedTypeVariant) {
|
||||
when (type.variant.features.serializer) {
|
||||
CONTEXTUAL, USE_CONTEXTUAL -> {
|
||||
val usage = type.classUsage
|
||||
appendLine("object ${type.serializerName}: ToDoSerializer<$usage>(\"${type.serialName}\")")
|
||||
}
|
||||
else -> Unit // no-op
|
||||
}
|
||||
}
|
||||
|
||||
private fun Appendable.writeUseSerializer(type: NamedTypeVariant) {
|
||||
when (type.variant.features.serializer) {
|
||||
CLASS_USE_SERIALIZER -> {
|
||||
val usage = type.classUsage
|
||||
appendLine("class ${type.useSerializer}: ToDoSerializer<$usage>(\"${type.serialName}\")")
|
||||
}
|
||||
else -> Unit // no-op
|
||||
}
|
||||
}
|
||||
|
||||
private const val TODO_SERIALIZER_NAME = "ToDoSerializer"
|
||||
|
||||
private const val TODO_SERIALIZER = """
|
||||
abstract class $TODO_SERIALIZER_NAME<T: Any>(descriptorName: String): KSerializer<T> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
|
||||
override fun deserialize(decoder: Decoder): T = TODO()
|
||||
override fun serialize(encoder: Encoder, value: T) = TODO()
|
||||
}
|
||||
"""
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.matrix.impl
|
||||
|
||||
import org.jetbrains.kotlinx.serialization.matrix.*
|
||||
import org.jetbrains.kotlinx.serialization.matrix.SerializerKind.*
|
||||
|
||||
internal const val CLASS_FOR_NESTED = "Container"
|
||||
|
||||
internal const val CLASS_FOR_INNER = "Outer"
|
||||
|
||||
internal const val SERIAL_ANNOTATION = "Extra"
|
||||
|
||||
|
||||
internal val TypeVariant.className: String
|
||||
get() = when (this) {
|
||||
is EnumVariant -> features.location.namePart + "Enum" + features.serializer.namePart
|
||||
}
|
||||
|
||||
internal val TypeVariant.elements: List<String>
|
||||
get() = when (this) {
|
||||
is EnumVariant -> options.entries.toList()
|
||||
}
|
||||
|
||||
internal val NamedTypeVariant.serialName: String
|
||||
get() = when (variant.features.serializer) {
|
||||
CUSTOM_CLASS, CUSTOM_OBJECT -> "custom|$classUsage"
|
||||
CONTEXTUAL, USE_CONTEXTUAL -> "contextual|$classUsage"
|
||||
CLASS_USE_SERIALIZER -> "useSerializer|$classUsage"
|
||||
else -> classUsage
|
||||
}
|
||||
|
||||
|
||||
internal val NamedTypeVariant.useSerializer: String?
|
||||
get() {
|
||||
if (variant.features.serializer != CLASS_USE_SERIALIZER) return null
|
||||
return serializerName
|
||||
}
|
||||
|
||||
internal val NamedTypeVariant.useContextualSerializer: String?
|
||||
get() {
|
||||
if (variant.features.serializer != USE_CONTEXTUAL) return null
|
||||
return serializerName
|
||||
}
|
||||
|
||||
internal val NamedTypeVariant.serializerName: String
|
||||
get() = when (variant.features.serializer) {
|
||||
CUSTOM_CLASS, CUSTOM_OBJECT, CLASS_USE_SERIALIZER, CONTEXTUAL, USE_CONTEXTUAL -> name + "Serializer"
|
||||
BY_DEFAULT -> throw IllegalStateException("No named serializer for type serializable by default '$name'")
|
||||
GENERATED -> throw IllegalStateException("No named serializer for type '$name' with automatically generated serializer")
|
||||
}
|
||||
|
||||
private val TypeLocation.namePart: String
|
||||
get() = when (this) {
|
||||
TypeLocation.FILE_ROOT -> ""
|
||||
TypeLocation.LOCAL -> "Local"
|
||||
TypeLocation.NESTED -> "Nested"
|
||||
}
|
||||
|
||||
private val SerializerKind.namePart: String
|
||||
get() = when (this) {
|
||||
BY_DEFAULT -> "WithDef"
|
||||
GENERATED -> ""
|
||||
CUSTOM_OBJECT -> "WithCustom"
|
||||
CUSTOM_CLASS -> "WithCustomCl"
|
||||
CONTEXTUAL -> "WithContextual"
|
||||
USE_CONTEXTUAL -> "WithUseContextual"
|
||||
CLASS_USE_SERIALIZER -> "WithUse"
|
||||
}
|
||||
Reference in New Issue
Block a user