diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/EnumReaderGenerator.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/EnumReaderGenerator.java index 9eba77af3..94bc30a68 100644 --- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/EnumReaderGenerator.java +++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/EnumReaderGenerator.java @@ -1,11 +1,14 @@ package ru.tinkoff.kora.json.annotation.processor.reader; import com.squareup.javapoet.*; +import ru.tinkoff.kora.annotation.processor.common.AnnotationUtils; import ru.tinkoff.kora.annotation.processor.common.CommonClassNames; import ru.tinkoff.kora.json.annotation.processor.JsonTypes; import ru.tinkoff.kora.json.annotation.processor.JsonUtils; import javax.annotation.Nullable; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import java.io.IOException; @@ -14,6 +17,7 @@ public class EnumReaderGenerator { public TypeSpec generateForEnum(TypeElement typeElement) { var typeName = ClassName.get(typeElement); + var enumValue = this.detectValueType(typeElement); var typeBuilder = TypeSpec.classBuilder(JsonUtils.jsonReaderName(typeElement)) .addAnnotation(AnnotationSpec.builder(CommonClassNames.koraGenerated) @@ -22,13 +26,13 @@ public TypeSpec generateForEnum(TypeElement typeElement) { .addSuperinterface(ParameterizedTypeName.get(JsonTypes.jsonReader, typeName)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addOriginatingElement(typeElement); - var delegateType = ParameterizedTypeName.get(JsonTypes.enumJsonReader, typeName); + var delegateType = ParameterizedTypeName.get(JsonTypes.enumJsonReader, typeName, enumValue.type.box()); typeBuilder.addField(delegateType, "delegate", Modifier.PRIVATE, Modifier.FINAL); typeBuilder.addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - // todo detect string representation method for enum - .addCode("this.delegate = new $T<>($T.values(), v -> v.toString());\n", JsonTypes.enumJsonReader, typeName) + .addParameter(ParameterizedTypeName.get(JsonTypes.jsonReader, enumValue.type.box()), "valueReader") + .addCode("this.delegate = new $T<>($T.values(), $T::$N, valueReader);\n", JsonTypes.enumJsonReader, typeName, typeName, enumValue.accessor) .build()); typeBuilder.addMethod(MethodSpec.methodBuilder("read") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) @@ -42,4 +46,22 @@ public TypeSpec generateForEnum(TypeElement typeElement) { ); return typeBuilder.build(); } + + record EnumValue(TypeName type, String accessor) {} + + private EnumValue detectValueType(TypeElement typeElement) { + for (var enclosedElement : typeElement.getEnclosedElements()) { + if (!enclosedElement.getModifiers().contains(Modifier.PUBLIC)) continue; + if (enclosedElement.getModifiers().contains(Modifier.STATIC)) continue; + if (enclosedElement.getKind() != ElementKind.METHOD) continue; + if (enclosedElement instanceof ExecutableElement executableElement && executableElement.getParameters().isEmpty()) { + if (AnnotationUtils.isAnnotationPresent(executableElement, JsonTypes.json)) { + var typeName = TypeName.get(executableElement.getReturnType()); + return new EnumValue(typeName, executableElement.getSimpleName().toString()); + } + } + } + var typeName = ClassName.get(String.class); + return new EnumValue(typeName, "toString"); + } } diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/EnumWriterGenerator.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/EnumWriterGenerator.java index ec6c9c12d..53273dec3 100644 --- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/EnumWriterGenerator.java +++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/EnumWriterGenerator.java @@ -1,11 +1,14 @@ package ru.tinkoff.kora.json.annotation.processor.writer; import com.squareup.javapoet.*; +import ru.tinkoff.kora.annotation.processor.common.AnnotationUtils; import ru.tinkoff.kora.annotation.processor.common.CommonClassNames; import ru.tinkoff.kora.json.annotation.processor.JsonTypes; import ru.tinkoff.kora.json.annotation.processor.JsonUtils; import javax.annotation.Nullable; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import java.io.IOException; @@ -21,12 +24,14 @@ public TypeSpec generateEnumWriter(TypeElement typeElement) { .addSuperinterface(ParameterizedTypeName.get(JsonTypes.jsonWriter, typeName)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addOriginatingElement(typeElement); - var delegateType = ParameterizedTypeName.get(JsonTypes.enumJsonWriter, typeName); + var enumValue = this.detectValueType(typeElement); + var delegateType = ParameterizedTypeName.get(JsonTypes.enumJsonWriter, typeName, enumValue.type.box()); typeBuilder.addField(delegateType, "delegate", Modifier.PRIVATE, Modifier.FINAL); typeBuilder.addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - .addCode("this.delegate = new $T<>($T.values(), v -> v.toString());\n", JsonTypes.enumJsonWriter, typeName) + .addParameter(ParameterizedTypeName.get(JsonTypes.jsonWriter, enumValue.type.box()), "valueWriter") + .addCode("this.delegate = new $T<>($T.values(), $T::$N, valueWriter);\n", JsonTypes.enumJsonWriter, typeName, typeName, enumValue.accessor) .build()); typeBuilder.addMethod(MethodSpec.methodBuilder("write") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) @@ -39,4 +44,22 @@ public TypeSpec generateEnumWriter(TypeElement typeElement) { ); return typeBuilder.build(); } + + record EnumValue(TypeName type, String accessor) {} + + private EnumValue detectValueType(TypeElement typeElement) { + for (var enclosedElement : typeElement.getEnclosedElements()) { + if (!enclosedElement.getModifiers().contains(Modifier.PUBLIC)) continue; + if (enclosedElement.getModifiers().contains(Modifier.STATIC)) continue; + if (enclosedElement.getKind() != ElementKind.METHOD) continue; + if (enclosedElement instanceof ExecutableElement executableElement && executableElement.getParameters().isEmpty()) { + if (AnnotationUtils.isAnnotationPresent(executableElement, JsonTypes.json)) { + var typeName = TypeName.get(executableElement.getReturnType()); + return new EnumValue(typeName, executableElement.getSimpleName().toString()); + } + } + } + var typeName = ClassName.get(String.class); + return new EnumValue(typeName, "toString"); + } } diff --git a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/EnumTest.java b/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/EnumTest.java index a8b45f958..7f59dc272 100644 --- a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/EnumTest.java +++ b/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/EnumTest.java @@ -1,6 +1,10 @@ package ru.tinkoff.kora.json.annotation.processor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; import org.junit.jupiter.api.Test; +import ru.tinkoff.kora.json.common.JsonReader; +import ru.tinkoff.kora.json.common.JsonWriter; import ru.tinkoff.kora.kora.app.annotation.processor.KoraAppProcessor; import java.util.List; @@ -8,6 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class EnumTest extends AbstractJsonAnnotationProcessorTest { + JsonReader stringReader = JsonParser::getValueAsString; + JsonWriter stringWriter = JsonGenerator::writeString; + @Test public void testEnum() { compile(""" @@ -19,11 +26,34 @@ public enum TestEnum { compileResult.assertSuccess(); - var mapper = mapper("TestEnum"); + var mapper = mapper("TestEnum", List.of(stringReader), List.of(stringWriter)); mapper.verify(enumConstant("TestEnum", "VALUE1"), "\"VALUE1\""); mapper.verify(enumConstant("TestEnum", "VALUE2"), "\"VALUE2\""); } + @Test + public void testEnumWithCustomJsonValue() { + compile(""" + @Json + public enum TestEnum { + VALUE1, VALUE2; + + @Json + public int intValue() { + return ordinal(); + } + } + """); + + compileResult.assertSuccess(); + JsonReader intReader = JsonParser::getIntValue; + JsonWriter intWriter = JsonGenerator::writeNumber; + + var mapper = mapper("TestEnum", List.of(intReader), List.of(intWriter)); + mapper.verify(enumConstant("TestEnum", "VALUE1"), "0"); + mapper.verify(enumConstant("TestEnum", "VALUE2"), "1"); + } + @Test public void testReaderFromExtension() { @@ -34,13 +64,16 @@ enum TestEnum { VALUE1, VALUE2 } + default ru.tinkoff.kora.json.common.JsonReader stringReader() { return com.fasterxml.jackson.core.JsonParser::getValueAsString; } + default ru.tinkoff.kora.json.common.JsonWriter stringWriter() { return com.fasterxml.jackson.core.JsonGenerator::writeString; } + @Root default String root(ru.tinkoff.kora.json.common.JsonReader r) {return "";} } """); compileResult.assertSuccess(); - assertThat(reader("TestApp_TestEnum")).isNotNull(); + assertThat(reader("TestApp_TestEnum", stringReader)).isNotNull(); } @Test @@ -51,14 +84,17 @@ public interface TestApp { enum TestEnum { VALUE1, VALUE2 } - + + default ru.tinkoff.kora.json.common.JsonReader stringReader() { return com.fasterxml.jackson.core.JsonParser::getValueAsString; } + default ru.tinkoff.kora.json.common.JsonWriter stringWriter() { return com.fasterxml.jackson.core.JsonGenerator::writeString; } + @Root default String root(ru.tinkoff.kora.json.common.JsonWriter r) {return "";} } """); compileResult.assertSuccess(); - assertThat(writer("TestApp_TestEnum")).isNotNull(); + assertThat(writer("TestApp_TestEnum", stringWriter)).isNotNull(); } @Test @@ -70,14 +106,17 @@ public interface TestApp { enum TestEnum { VALUE1, VALUE2 } - + + default ru.tinkoff.kora.json.common.JsonReader stringReader() { return com.fasterxml.jackson.core.JsonParser::getValueAsString; } + default ru.tinkoff.kora.json.common.JsonWriter stringWriter() { return com.fasterxml.jackson.core.JsonGenerator::writeString; } + @Root default String root(ru.tinkoff.kora.json.common.JsonReader r) {return "";} } """); compileResult.assertSuccess(); - assertThat(reader("TestApp_TestEnum")).isNotNull(); + assertThat(reader("TestApp_TestEnum", stringReader)).isNotNull(); } @Test @@ -89,13 +128,16 @@ public interface TestApp { enum TestEnum { VALUE1, VALUE2 } - + + default ru.tinkoff.kora.json.common.JsonReader stringReader() { return com.fasterxml.jackson.core.JsonParser::getValueAsString; } + default ru.tinkoff.kora.json.common.JsonWriter stringWriter() { return com.fasterxml.jackson.core.JsonGenerator::writeString; } + @Root default String root(ru.tinkoff.kora.json.common.JsonWriter r) {return "";} } """); compileResult.assertSuccess(); - assertThat(writer("TestApp_TestEnum")).isNotNull(); + assertThat(writer("TestApp_TestEnum", stringWriter)).isNotNull(); } } diff --git a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/JsonAnnotationProcessorTest.java b/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/JsonAnnotationProcessorTest.java index 168789f24..5020a109d 100644 --- a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/JsonAnnotationProcessorTest.java +++ b/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/JsonAnnotationProcessorTest.java @@ -321,22 +321,6 @@ void testNullableBeans() throws Exception { .hasMessageStartingWith("Expecting [VALUE_NUMBER_INT] token for field 'field4', got VALUE_NULL"); } - @Test - void testEnum() throws Exception { - var cl = processClass0(DtoWithEnum.class); - var reader = cl.reader(DtoWithEnum.class, new EnumJsonReader<>(DtoWithEnum.TestEnum.values(), Enum::name)); - var writer = cl.writer(DtoWithEnum.class, cl.writer(DtoWithEnum.TestEnum.class)); - - var expected = new DtoWithEnum(DtoWithEnum.TestEnum.VAL1); - var json = """ - { - "testEnum" : "VAL1" - }"""; - - assertThat(fromJson(reader, json)).isEqualTo(expected); - assertThat(toJson(writer, expected)).isEqualTo(json); - } - @Test void testObject() throws Exception { var cl = processClass0(DtoWithObject.class); diff --git a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/dto/DtoWithEnum.java b/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/dto/DtoWithEnum.java deleted file mode 100644 index af865a2e9..000000000 --- a/json/json-annotation-processor/src/test/java/ru/tinkoff/kora/json/annotation/processor/dto/DtoWithEnum.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.tinkoff.kora.json.annotation.processor.dto; - -import ru.tinkoff.kora.json.common.annotation.Json; - -@Json -public record DtoWithEnum(TestEnum testEnum) { - @Json - public enum TestEnum { - VAL1, VAL2 - } -} diff --git a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonReader.java b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonReader.java index 6cf5f114b..894ebd912 100644 --- a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonReader.java +++ b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonReader.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; import javax.annotation.Nullable; import java.io.IOException; @@ -10,30 +9,28 @@ import java.util.Map; import java.util.function.Function; -public final class EnumJsonReader> implements JsonReader { - private final Map values; +public final class EnumJsonReader, V> implements JsonReader { + private final Map values; + private final JsonReader valueReader; - public EnumJsonReader(T[] values, Function mapper) { + public EnumJsonReader(T[] values, Function mapper, JsonReader valueReader) { this.values = new HashMap<>(); for (var value : values) { this.values.put(mapper.apply(value), value); } + this.valueReader = valueReader; } @Nullable @Override public T read(JsonParser parser) throws IOException { - var token = parser.currentToken(); - if (token == JsonToken.VALUE_NULL) { + var jsonValue = this.valueReader.read(parser); + if (jsonValue == null) { return null; } - if (token != JsonToken.VALUE_STRING) { - throw new JsonParseException(parser, "Expecting VALUE_STRING token, got " + token); - } - var stringValue = parser.getText(); - var value = this.values.get(stringValue); + var value = this.values.get(jsonValue); if (value == null) { - throw new JsonParseException(parser, "Expecting one of " + this.values.keySet() + ", got " + stringValue); + throw new JsonParseException(parser, "Expecting one of " + this.values.keySet() + ", got " + jsonValue); } return value; } diff --git a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonWriter.java b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonWriter.java index d058ab82d..91bd80ceb 100644 --- a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonWriter.java +++ b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/EnumJsonWriter.java @@ -1,19 +1,26 @@ package ru.tinkoff.kora.json.common; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.io.SerializedString; import javax.annotation.Nullable; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.function.Function; -public final class EnumJsonWriter> implements JsonWriter { - private final SerializedString[] values; +public final class EnumJsonWriter, V> implements JsonWriter { + private final RawJson[] values; - public EnumJsonWriter(T[] values, Function mapper) { - this.values = new SerializedString[values.length]; + public EnumJsonWriter(T[] values, Function valueExtractor, JsonWriter valueWriter) { + this.values = new RawJson[values.length]; for (int i = 0; i < values.length; i++) { - this.values[i] = new SerializedString(mapper.apply(values[i])); + var enumValue = values[i]; + var value = valueExtractor.apply(enumValue); + try { + var bytes = valueWriter.toByteArray(value); + this.values[i] = new RawJson(bytes); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } } @@ -21,8 +28,8 @@ public EnumJsonWriter(T[] values, Function mapper) { public void write(JsonGenerator gen, @Nullable T object) throws IOException { if (object == null) { gen.writeNull(); - } else { - gen.writeString(this.values[object.ordinal()]); + return; } + gen.writeRawValue(this.values[object.ordinal()]); } } diff --git a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJson.java b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJson.java index 42aa8d623..a3af23dec 100644 --- a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJson.java +++ b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJson.java @@ -8,9 +8,20 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -public record RawJson(@Nonnull byte[] value) implements SerializableString { +public class RawJson implements SerializableString { + public final byte[] value; + private char[] valueChars; + public RawJson(@Nonnull String value) { - this(value.getBytes(StandardCharsets.UTF_8)); + this.value = value.getBytes(StandardCharsets.UTF_8); + } + + public RawJson(@Nonnull byte[] value) { + this.value = value; + } + + public byte[] value() { + return value; } @Override @@ -60,7 +71,15 @@ public int appendUnquotedUTF8(byte[] buffer, int offset) { @Override public int appendUnquoted(char[] buffer, int offset) { - throw new UnsupportedOperationException(); + if (valueChars == null) { + valueChars = new String(value).toCharArray(); + } + final int length = valueChars.length; + if ((offset + length) > buffer.length) { + return -1; + } + System.arraycopy(valueChars, 0, buffer, offset, length); + return length; } @Override diff --git a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJsonWriter.java b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJsonWriter.java index 0c9f85e19..ad0cbf73b 100644 --- a/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJsonWriter.java +++ b/json/json-common/src/main/java/ru/tinkoff/kora/json/common/RawJsonWriter.java @@ -21,6 +21,6 @@ public byte[] toByteArray(@Nullable RawJson object) throws IOException { if (object == null) { return "null".getBytes(StandardCharsets.ISO_8859_1); } - return object.value(); + return object.value; } } diff --git a/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/reader/EnumJsonReaderGenerator.kt b/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/reader/EnumJsonReaderGenerator.kt index 1e96c71b7..0ff77d7aa 100644 --- a/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/reader/EnumJsonReaderGenerator.kt +++ b/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/reader/EnumJsonReaderGenerator.kt @@ -1,13 +1,15 @@ package ru.tinkoff.kora.json.ksp.reader +import com.google.devtools.ksp.isPublic import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.ksp.addOriginatingKSFile import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.toTypeName import ru.tinkoff.kora.json.ksp.JsonTypes import ru.tinkoff.kora.json.ksp.jsonReaderName +import ru.tinkoff.kora.ksp.common.AnnotationUtils.isAnnotationPresent import ru.tinkoff.kora.ksp.common.KspCommonUtils.generated import ru.tinkoff.kora.ksp.common.KspCommonUtils.toTypeName @@ -15,14 +17,32 @@ class EnumJsonReaderGenerator { fun generateEnumReader(jsonClassDeclaration: KSClassDeclaration): TypeSpec { val className = jsonClassDeclaration.toClassName() val typeName = jsonClassDeclaration.toTypeName() + val enumType = detectValueType(jsonClassDeclaration) + val typeBuilder = TypeSpec.classBuilder(jsonClassDeclaration.jsonReaderName()) .generated(JsonReaderGenerator::class) + .primaryConstructor(FunSpec.constructorBuilder() + .addParameter("valueReader", JsonTypes.jsonReader.parameterizedBy(enumType.type)) + .build() + ) .addSuperinterface( JsonTypes.jsonReader.parameterizedBy(typeName), - CodeBlock.of("%T(%T.values(), { it.toString() })", JsonTypes.enumJsonReader, className) - ) // todo detect to string method + CodeBlock.of("%T(%T.values(), %T::%N, valueReader)", JsonTypes.enumJsonReader, className, className, enumType.accessor) + ) jsonClassDeclaration.containingFile?.let { typeBuilder.addOriginatingKSFile(it) } return typeBuilder.build() + } + + data class EnumValue(val type: TypeName, val accessor: String) + fun detectValueType(typeElement: KSClassDeclaration): EnumValue { + for (function in typeElement.getAllFunctions()) { + if (function.isPublic() && function.parameters.isEmpty() && function.isAnnotationPresent(JsonTypes.json)) { + val typeName = function.returnType!!.toTypeName() + return EnumValue(typeName, function.simpleName.asString()) + } + } + val typeName = String::class.asTypeName() + return EnumValue(typeName, "toString") } } diff --git a/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/writer/EnumJsonWriterGenerator.kt b/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/writer/EnumJsonWriterGenerator.kt index c81e3b6e3..5477f29de 100644 --- a/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/writer/EnumJsonWriterGenerator.kt +++ b/json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/writer/EnumJsonWriterGenerator.kt @@ -1,13 +1,15 @@ package ru.tinkoff.kora.json.ksp.writer +import com.google.devtools.ksp.isPublic import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.ksp.addOriginatingKSFile import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.toTypeName import ru.tinkoff.kora.json.ksp.JsonTypes import ru.tinkoff.kora.json.ksp.jsonWriterName +import ru.tinkoff.kora.ksp.common.AnnotationUtils.isAnnotationPresent import ru.tinkoff.kora.ksp.common.KspCommonUtils.generated import ru.tinkoff.kora.ksp.common.KspCommonUtils.toTypeName @@ -15,13 +17,33 @@ class EnumJsonWriterGenerator { fun generateEnumWriter(jsonClassDeclaration: KSClassDeclaration): TypeSpec { val className = jsonClassDeclaration.toClassName() val typeName = jsonClassDeclaration.toTypeName() + val enumType = detectValueType(jsonClassDeclaration) + val typeBuilder = TypeSpec.classBuilder(jsonClassDeclaration.jsonWriterName()) .generated(JsonWriterGenerator::class) + .primaryConstructor(FunSpec.constructorBuilder() + .addParameter("valueWriter", JsonTypes.jsonWriter.parameterizedBy(enumType.type)) + .build() + ) .addSuperinterface( JsonTypes.jsonWriter.parameterizedBy(typeName), - CodeBlock.of("%T(%T.values(), { it.toString() })", JsonTypes.enumJsonWriter, className) - ) // todo detect to string method + CodeBlock.of("%T(%T.values(), %T::%N, valueWriter)", JsonTypes.enumJsonWriter, className, className, enumType.accessor) + ) jsonClassDeclaration.containingFile?.let { typeBuilder.addOriginatingKSFile(it) } return typeBuilder.build() } + + + data class EnumValue(val type: TypeName, val accessor: String) + + fun detectValueType(typeElement: KSClassDeclaration): EnumValue { + for (function in typeElement.getAllFunctions()) { + if (function.isPublic() && function.parameters.isEmpty() && function.isAnnotationPresent(JsonTypes.json)) { + val typeName = function.returnType!!.toTypeName() + return EnumValue(typeName, function.simpleName.asString()) + } + } + val typeName = String::class.asTypeName() + return EnumValue(typeName, "toString") + } } diff --git a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/AbstractJsonSymbolProcessorTest.kt b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/AbstractJsonSymbolProcessorTest.kt index 994bb26e9..70d01279b 100644 --- a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/AbstractJsonSymbolProcessorTest.kt +++ b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/AbstractJsonSymbolProcessorTest.kt @@ -28,8 +28,8 @@ abstract class AbstractJsonSymbolProcessorTest : AbstractSymbolProcessorTest() { protected open fun readerClass(forClass: String) = compileResult.classLoader.readerClass(testPackage(), forClass) protected open fun writerClass(forClass: String) = compileResult.classLoader.writerClass(testPackage(), forClass) - protected open fun reader(forClass: String, vararg params: Any?) = compileResult.classLoader.reader(testPackage(), forClass, params) - protected open fun writer(forClass: String, vararg params: Any?) = compileResult.classLoader.writer(testPackage(), forClass, params) + protected open fun reader(forClass: String, vararg params: Any?) = compileResult.classLoader.reader(testPackage(), forClass, *params) + protected open fun writer(forClass: String, vararg params: Any?) = compileResult.classLoader.writer(testPackage(), forClass, *params) protected open fun mapper(forClass: String) = compileResult.classLoader.mapper(testPackage(), forClass) protected open fun mapper(forClass: String, readerParams: List<*>, writerParams: List<*>) = compileResult.classLoader.mapper(testPackage(), forClass, readerParams, writerParams) diff --git a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/EnumTest.kt b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/EnumTest.kt new file mode 100644 index 000000000..564dc8377 --- /dev/null +++ b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/EnumTest.kt @@ -0,0 +1,147 @@ +package ru.tinkoff.kora.json.ksp + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import ru.tinkoff.kora.json.common.JsonReader +import ru.tinkoff.kora.json.common.JsonWriter +import ru.tinkoff.kora.kora.app.ksp.KoraAppProcessorProvider + +class EnumTest : AbstractJsonSymbolProcessorTest() { + private var stringReader = JsonReader { obj: JsonParser -> obj.valueAsString } + private var stringWriter = JsonWriter { obj: JsonGenerator, text: String? -> obj.writeString(text) } + + @Test + fun testEnum() { + compile(""" + @Json + enum class TestEnum { + VALUE1, VALUE2 + } + + """.trimIndent()) + compileResult.assertSuccess() + val mapper = mapper("TestEnum", listOf(stringReader), listOf(stringWriter)) + mapper.assert(enumConstant("TestEnum", "VALUE1"), "\"VALUE1\"") + mapper.assert(enumConstant("TestEnum", "VALUE2"), "\"VALUE2\"") + } + + @Test + fun testEnumWithCustomJsonValue() { + compile(""" + @Json + enum class TestEnum { + VALUE1, VALUE2; + + @Json + fun intValue() = ordinal + } + """.trimIndent()) + compileResult.assertSuccess() + val intReader = JsonReader { obj: JsonParser -> obj.intValue } + val intWriter = JsonWriter { obj: JsonGenerator, v: Int? -> obj.writeNumber(v!!) } + val mapper = mapper("TestEnum", listOf(intReader), listOf(intWriter)) + mapper.assert(enumConstant("TestEnum", "VALUE1"), "0") + mapper.assert(enumConstant("TestEnum", "VALUE2"), "1") + } + + + @Test + fun testReaderFromExtension() { + compile(listOf(KoraAppProcessorProvider()), """ + @ru.tinkoff.kora.common.KoraApp + interface TestApp { + enum class TestEnum { + VALUE1, VALUE2 + } + + fun stringReader(): ru.tinkoff.kora.json.common.JsonReader = ru.tinkoff.kora.json.common.JsonReader { obj -> obj.valueAsString } + fun stringWriter(): ru.tinkoff.kora.json.common.JsonWriter = ru.tinkoff.kora.json.common.JsonWriter { obj, text -> obj.writeString(text) } + + @Root + fun root(r: ru.tinkoff.kora.json.common.JsonReader) = "" + } + + """.trimIndent()) + compileResult.assertSuccess() + Assertions.assertThat(reader("TestApp_TestEnum", stringReader)).isNotNull() + } + + @Test + fun testWriterFromExtension() { + compile(listOf(KoraAppProcessorProvider()), """ + @ru.tinkoff.kora.common.KoraApp + interface TestApp { + enum class TestEnum { + VALUE1, VALUE2 + } + + fun stringReader(): ru.tinkoff.kora.json.common.JsonReader = ru.tinkoff.kora.json.common.JsonReader { obj -> obj.valueAsString } + fun stringWriter(): ru.tinkoff.kora.json.common.JsonWriter = ru.tinkoff.kora.json.common.JsonWriter { obj, text -> obj.writeString(text) } + + @Root + fun root(r: ru.tinkoff.kora.json.common.JsonWriter) = "" + } + + """.trimIndent()) + compileResult.assertSuccess() + Assertions.assertThat(writer("TestApp_TestEnum", stringWriter)).isNotNull() + } + + @Test + fun testAnnotationProcessedReaderFromExtension() { + compile(listOf(KoraAppProcessorProvider(), JsonSymbolProcessorProvider()), """ + @ru.tinkoff.kora.common.KoraApp + public interface TestApp { + @Json + enum class TestEnum { + VALUE1, VALUE2 + } + + fun stringReader(): ru.tinkoff.kora.json.common.JsonReader = ru.tinkoff.kora.json.common.JsonReader { obj -> obj.valueAsString } + fun stringWriter(): ru.tinkoff.kora.json.common.JsonWriter = ru.tinkoff.kora.json.common.JsonWriter { obj, text -> obj.writeString(text) } + + @Root + fun root(r: ru.tinkoff.kora.json.common.JsonReader) = "" + } + + """.trimIndent()) + compileResult.assertSuccess() + Assertions.assertThat(reader("TestApp_TestEnum", stringReader)).isNotNull() + } + + @Test + fun testAnnotationProcessedWriterFromExtension() { + compile(listOf(KoraAppProcessorProvider(), JsonSymbolProcessorProvider()), """ + @ru.tinkoff.kora.common.KoraApp + interface TestApp { + @Json + enum class TestEnum { + VALUE1, VALUE2 + } + + fun stringReader(): ru.tinkoff.kora.json.common.JsonReader = ru.tinkoff.kora.json.common.JsonReader { obj -> obj.valueAsString } + fun stringWriter(): ru.tinkoff.kora.json.common.JsonWriter = ru.tinkoff.kora.json.common.JsonWriter { obj, text -> obj.writeString(text) } + + @Root + fun root(r: ru.tinkoff.kora.json.common.JsonWriter) = "" + } + + """.trimIndent()) + compileResult.assertSuccess() + Assertions.assertThat(writer("TestApp_TestEnum", stringWriter)).isNotNull() + } + + private fun enumConstant(className: String, name: String): Any { + val clazz = this.compileResult.loadClass(className); + require(clazz.isEnum) + for (enumConstant in clazz.enumConstants) { + val e = enumConstant as Enum<*> + if (e.name == name) { + return e; + } + } + throw RuntimeException("Invalid enum constant: $name"); + } +} diff --git a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/JsonAnnotationProcessorTest.kt b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/JsonAnnotationProcessorTest.kt index ccc2a6172..3f8306acb 100644 --- a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/JsonAnnotationProcessorTest.kt +++ b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/JsonAnnotationProcessorTest.kt @@ -32,8 +32,8 @@ internal class JsonAnnotationProcessorTest { ) val writer: JsonWriter = cl.writer( DtoWithSupportedTypes::class.java, ListJsonWriter { obj: JsonGenerator, v: Int? -> - obj.writeNumber(v!!) - }, + obj.writeNumber(v!!) + }, SetJsonWriter { obj: JsonGenerator, v: Int? -> obj.writeNumber( v!! @@ -310,27 +310,6 @@ internal class JsonAnnotationProcessorTest { .hasMessageStartingWith("Expecting [VALUE_NUMBER_INT] token for field 'field4', got VALUE_NULL") } - - @Test - fun testEnum() { - val cl = jsonClassLoader(DtoWithEnum::class) - val reader = cl.reader( - DtoWithEnum::class.java, - cl.reader(DtoWithEnum.TestEnum::class.java) - ) - val writer = cl.writer( - DtoWithEnum::class.java, - cl.writer(DtoWithEnum.TestEnum::class.java) - ) - val expected = DtoWithEnum(DtoWithEnum.TestEnum.VAL1) - val json: String = """ - { - "testEnum" : "VAL1" - }""".trimIndent() - assertThat(fromJson(reader, json)).isEqualTo(expected) - assertThat(toJson(writer, expected)).isEqualTo(json) - } - @Test fun testKotlinDataClassDto() { val jsoner = jsonClassLoader(KotlinDataClassDtoWithNonPrimaryConstructor::class) diff --git a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/dto/DtoWithEnum.kt b/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/dto/DtoWithEnum.kt deleted file mode 100644 index f03610fd6..000000000 --- a/json/json-symbol-processor/src/test/kotlin/ru/tinkoff/kora/json/ksp/dto/DtoWithEnum.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.tinkoff.kora.json.ksp.dto - -import ru.tinkoff.kora.json.common.annotation.Json - -@Json -data class DtoWithEnum(val testEnum: TestEnum) { - @Json - enum class TestEnum { - VAL1, VAL2 - } -} diff --git a/openapi/openapi-generator/src/main/java/ru/tinkoff/kora/openapi/generator/KoraCodegen.java b/openapi/openapi-generator/src/main/java/ru/tinkoff/kora/openapi/generator/KoraCodegen.java index 006045bc5..ca99a6c08 100644 --- a/openapi/openapi-generator/src/main/java/ru/tinkoff/kora/openapi/generator/KoraCodegen.java +++ b/openapi/openapi-generator/src/main/java/ru/tinkoff/kora/openapi/generator/KoraCodegen.java @@ -1599,7 +1599,7 @@ public String toEnumVarName(String value, String datatype) { } // number - if ("Integer".equals(datatype) || "Long".equals(datatype) || + if ("Integer".equals(datatype) || "Int".equals(datatype) || "Long".equals(datatype) || "Float".equals(datatype) || "Double".equals(datatype) || "BigDecimal".equals(datatype)) { String varName = "NUMBER_" + value; varName = varName.replaceAll("-", "MINUS_"); @@ -1619,7 +1619,7 @@ public String toEnumVarName(String value, String datatype) { @Override public String toEnumValue(String value, String datatype) { - if ("Integer".equals(datatype) || "Double".equals(datatype)) { + if ("Integer".equals(datatype) || "Int".equals(datatype) || "Double".equals(datatype)) { return value; } else if ("Long".equals(datatype)) { // add l to number, e.g. 2048 => 2048l diff --git a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaEnumClass.mustache b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaEnumClass.mustache index e1d7a3106..7b120dbbc 100644 --- a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaEnumClass.mustache +++ b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaEnumClass.mustache @@ -26,10 +26,15 @@ @ru.tinkoff.kora.common.Component public static final class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonWriter implements ru.tinkoff.kora.json.common.JsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { - private final ru.tinkoff.kora.json.common.EnumJsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> delegate = new ru.tinkoff.kora.json.common.EnumJsonWriter<>( - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue - ); + private final ru.tinkoff.kora.json.common.EnumJsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}, {{{dataType}}}> delegate; + + public {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonWriter(ru.tinkoff.kora.json.common.JsonWriter<{{{dataType}}}> valueWriter) { + this.delegate = new ru.tinkoff.kora.json.common.EnumJsonWriter<>( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueWriter + ); + } @Override public void write(com.fasterxml.jackson.core.JsonGenerator gen, {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} value) throws java.io.IOException { @@ -39,10 +44,15 @@ @ru.tinkoff.kora.common.Component public static final class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonReader implements ru.tinkoff.kora.json.common.JsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { - private final ru.tinkoff.kora.json.common.EnumJsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> delegate = new ru.tinkoff.kora.json.common.EnumJsonReader<>( - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue - ); + private final ru.tinkoff.kora.json.common.EnumJsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}, {{{dataType}}}> delegate; + + public {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonReader(ru.tinkoff.kora.json.common.JsonReader<{{{dataType}}}> valueReader) { + this.delegate = new ru.tinkoff.kora.json.common.EnumJsonReader<>( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueReader + ); + } @Override @javax.annotation.Nullable @@ -56,7 +66,7 @@ public static final class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}StringParameterReader implements ru.tinkoff.kora.http.server.common.handler.StringParameterReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { private final ru.tinkoff.kora.http.server.common.handler.EnumStringParameterReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> delegate = new ru.tinkoff.kora.http.server.common.handler.EnumStringParameterReader<>( {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue + v -> String.valueOf(v.value) ); public {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} read(String string) { return this.delegate.read(string); @@ -68,7 +78,7 @@ public static final class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}StringParameterConverter implements ru.tinkoff.kora.http.client.common.writer.StringParameterConverter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { private final ru.tinkoff.kora.http.client.common.writer.EnumStringParameterConverter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> delegate = new ru.tinkoff.kora.http.client.common.writer.EnumStringParameterConverter<>( {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue + v -> String.valueOf(v.value) ); public String convert({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} object) { diff --git a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaModel.mustache b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaModel.mustache index 0b09120d3..3ba1a258d 100644 --- a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaModel.mustache +++ b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/javaModel.mustache @@ -18,7 +18,8 @@ import ru.tinkoff.kora.json.common.annotation.*; {{^isEnum}} /** - * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}} + * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}}{{#allVars}} + * @param {{name}} {{#description}}{{.}}{{/description}}{{^description}}{{name}}{{/description}}{{/allVars}} */ {{#discriminator}} @ru.tinkoff.kora.json.common.annotation.Json diff --git a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinEnumClass.mustache b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinEnumClass.mustache index 75721e047..1059058c5 100644 --- a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinEnumClass.mustache +++ b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinEnumClass.mustache @@ -27,10 +27,13 @@ } @ru.tinkoff.kora.common.Component - class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonWriter : ru.tinkoff.kora.json.common.JsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { - private val delegate = ru.tinkoff.kora.json.common.EnumJsonWriter({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { - it.getValue() - } + class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonWriter(valueWriter: ru.tinkoff.kora.json.common.JsonWriter<{{{dataType}}}>) + : ru.tinkoff.kora.json.common.JsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { + private val delegate = ru.tinkoff.kora.json.common.EnumJsonWriter( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueWriter + ) override fun write(gen: com.fasterxml.jackson.core.JsonGenerator, value: {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?) { this.delegate.write(gen, value) @@ -38,20 +41,26 @@ } @ru.tinkoff.kora.common.Component - class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonReader : ru.tinkoff.kora.json.common.JsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { - private val delegate = ru.tinkoff.kora.json.common.EnumJsonReader({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { - it.getValue() - } + class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}JsonReader(valueReader: ru.tinkoff.kora.json.common.JsonReader<{{{dataType}}}>) + : ru.tinkoff.kora.json.common.JsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { + private val delegate = ru.tinkoff.kora.json.common.EnumJsonReader( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueReader + ) override fun read(parser: com.fasterxml.jackson.core.JsonParser) : {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} { return this.delegate.read(parser)!! } } @ru.tinkoff.kora.common.Component - class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}NullableJsonWriter : ru.tinkoff.kora.json.common.JsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?> { - private val delegate = ru.tinkoff.kora.json.common.EnumJsonWriter({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { - it.getValue() - } + class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}NullableJsonWriter(valueWriter: ru.tinkoff.kora.json.common.JsonWriter<{{{dataType}}}>) + : ru.tinkoff.kora.json.common.JsonWriter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?> { + private val delegate = ru.tinkoff.kora.json.common.EnumJsonWriter( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueWriter + ) override fun write(gen: com.fasterxml.jackson.core.JsonGenerator, value: {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?) { this.delegate.write(gen, value) @@ -59,10 +68,13 @@ } @ru.tinkoff.kora.common.Component - class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}NullableJsonReader : ru.tinkoff.kora.json.common.JsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?> { - private val delegate = ru.tinkoff.kora.json.common.EnumJsonReader({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { - it.getValue() - } + class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}NullableJsonReader(valueReader: ru.tinkoff.kora.json.common.JsonReader<{{{dataType}}}>) + : ru.tinkoff.kora.json.common.JsonReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}?> { + private val delegate = ru.tinkoff.kora.json.common.EnumJsonReader( + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}::getValue, + valueReader + ) override fun read(parser: com.fasterxml.jackson.core.JsonParser) : {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}? { return this.delegate.read(parser) @@ -73,7 +85,7 @@ class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}StringParameterReader : ru.tinkoff.kora.http.server.common.handler.StringParameterReader<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { private val delegate = ru.tinkoff.kora.http.server.common.handler.EnumStringParameterReader( {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - { it.getValue() } + { it.getValue().toString() } ) override fun read(string: String): {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} { @@ -86,7 +98,7 @@ class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}StringParameterConverter : ru.tinkoff.kora.http.client.common.writer.StringParameterConverter<{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { private val delegate = ru.tinkoff.kora.http.client.common.writer.EnumStringParameterConverter( {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values(), - { it.getValue() } + { it.getValue().toString() } ) override fun convert(value: {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}): String { diff --git a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinModel.mustache b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinModel.mustache index f31086f6b..28f505669 100644 --- a/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinModel.mustache +++ b/openapi/openapi-generator/src/main/resources/openapi/templates/kora/kotlinModel.mustache @@ -20,7 +20,8 @@ import ru.tinkoff.kora.json.common.EnumJsonWriter {{^isEnum}} /** - * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}} + * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}}{{#allVars}} + * @param {{name}} {{#description}}{{.}}{{/description}}{{^description}}{{name}}{{/description}}{{/allVars}} */ {{#discriminator}} @ru.tinkoff.kora.json.common.annotation.Json diff --git a/openapi/openapi-generator/src/test/resources/example/petstoreV3_discriminator.yaml b/openapi/openapi-generator/src/test/resources/example/petstoreV3_discriminator.yaml index 21bf4c201..d253f02a7 100644 --- a/openapi/openapi-generator/src/test/resources/example/petstoreV3_discriminator.yaml +++ b/openapi/openapi-generator/src/test/resources/example/petstoreV3_discriminator.yaml @@ -66,6 +66,10 @@ components: breed: type: string enum: [ Dingo, Husky, Retriever, Shepherd ] + int-breed: + description: "enum with int value" + type: int + enum: [ 5, 7, 8, 10 ] Cat: # "Cat" is a value for the pet_type property (the discriminator value) allOf: # Combines the main `Pet` schema with `Cat`-specific properties - $ref: '#/components/schemas/Pet' diff --git a/symbol-processor-common/src/testFixtures/kotlin/ru/tinkoff/kora/ksp/common/AbstractSymbolProcessorTest.kt b/symbol-processor-common/src/testFixtures/kotlin/ru/tinkoff/kora/ksp/common/AbstractSymbolProcessorTest.kt index f2b26b3b0..84ad290ce 100644 --- a/symbol-processor-common/src/testFixtures/kotlin/ru/tinkoff/kora/ksp/common/AbstractSymbolProcessorTest.kt +++ b/symbol-processor-common/src/testFixtures/kotlin/ru/tinkoff/kora/ksp/common/AbstractSymbolProcessorTest.kt @@ -115,7 +115,7 @@ abstract class AbstractSymbolProcessorTest { fun compilationException(): Throwable { val errorMessages = mutableListOf() - val indexOfFirst = messages.indexOfFirst { it.startsWith("e: [ksp]") } + val indexOfFirst = messages.indexOfFirst { it.startsWith("e: ") } if (indexOfFirst >= 0) { for (i in indexOfFirst until messages.size) { val message = messages[i]