From 3059341424f3bd6ed2c6482352f2f0aebd3b4db5 Mon Sep 17 00:00:00 2001 From: Alexxand Date: Mon, 17 Jul 2023 15:52:25 +0300 Subject: [PATCH] Add tests for scala3 enum derivation and fix writer derivation for scala3 enums (#224) * Add unit tests for scala3 enums * Fix derivation for scala3 enums Enums should be converted into StringValueNode without object start and object end, but when discriminator is used, it should be converted into object with discriminator --------- Co-authored-by: Aleksandr Petukhov --- .../impl/derivation/WriterDerivation.scala | 20 ++++----- .../derivation/AutoReaderDerivationTest.scala | 22 +++++++++- .../derivation/AutoWriterDerivationTest.scala | 12 +++++- .../SemiautoReaderDerivationTest.scala | 31 ++++++++++++- .../SemiautoWriterDerivationTest.scala | 43 ++++++++++++++++++- .../scala-3/tethys/derivation/package.scala | 9 ++++ 6 files changed, 123 insertions(+), 14 deletions(-) diff --git a/modules/macro-derivation/src/main/scala-3/tethys/derivation/impl/derivation/WriterDerivation.scala b/modules/macro-derivation/src/main/scala-3/tethys/derivation/impl/derivation/WriterDerivation.scala index 64469f35..47f1b36b 100644 --- a/modules/macro-derivation/src/main/scala-3/tethys/derivation/impl/derivation/WriterDerivation.scala +++ b/modules/macro-derivation/src/main/scala-3/tethys/derivation/impl/derivation/WriterDerivation.scala @@ -3,12 +3,12 @@ package tethys.derivation.impl.derivation import scala.annotation.tailrec import scala.compiletime.* import scala.quoted.* - import tethys.commons.LowPriorityInstance +import tethys.commons.TokenNode.FieldNameNode import tethys.derivation.builder.WriterDerivationConfig import tethys.derivation.impl.builder.{WriterBuilderCommons, WriterBuilderUtils} import tethys.derivation.impl.FieldStyle -import tethys.writers.tokens.TokenWriter +import tethys.writers.tokens.{SimpleTokenWriter, TokenWriter} import tethys.{JsonObjectWriter, JsonWriter} trait WriterDerivation extends WriterBuilderCommons { @@ -436,16 +436,16 @@ trait WriterDerivation extends WriterBuilderCommons { .getWrite3Method .appliedTo(Expr(discriminator).asTerm, termChildNameTerm, tokenWriterTerm) } + val writeObjectStartTerm = tokenWriterTerm.selectFirstMethod("writeObjectStart").appliedToNone + val writeObjectEndTerm = tokenWriterTerm.selectFirstMethod("writeObjectEnd").appliedToNone val terms: List[Term] = - if (termChildWriter.underlying.symbol.flags.is(Flags.Macro)) - List( - termChildWriter.selectWriteValuesMethod.appliedTo(termChildRef, tokenWriterTerm), - discriminatorTerm - ) - else { - val writeObjectStartTerm = tokenWriterTerm.selectFirstMethod("writeObjectStart").appliedToNone + if (termChildSym.flags.is(Flags.Enum)) { + if (discriminator.isEmpty) + List(termChildWriter.selectWriteValuesMethod.appliedTo(termChildRef, tokenWriterTerm)) + else + List(writeObjectStartTerm, discriminatorTerm, writeObjectEndTerm) + } else { val writeValuesTerm = termChildWriter.selectWriteValuesMethod.appliedTo(termChildRef, tokenWriterTerm) - val writeObjectEndTerm = tokenWriterTerm.selectFirstMethod("writeObjectEnd").appliedToNone List(writeObjectStartTerm, writeValuesTerm, discriminatorTerm, writeObjectEndTerm) } val rhs = Block(terms, '{ () }.asTerm) diff --git a/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoReaderDerivationTest.scala b/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoReaderDerivationTest.scala index 2b18f358..1f2e0d7c 100644 --- a/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoReaderDerivationTest.scala +++ b/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoReaderDerivationTest.scala @@ -4,7 +4,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec import tethys.JsonReader import tethys.commons.{Token, TokenNode} -import tethys.commons.TokenNode.* +import tethys.commons.TokenNode.{value => token, *} import tethys.derivation.auto.* import tethys.readers.ReaderError import tethys.readers.tokens.QueueIterator @@ -66,4 +66,24 @@ class AutoReaderDerivationTest extends AnyFlatSpec with Matchers { ) )) shouldBe ComplexRecursionA(1, Some(ComplexRecursionB(2, ComplexRecursionA(3, None)))) } + + it should "derive reader for simple enum" in { + read[SimpleEnum]( + token(SimpleEnum.ONE.toString) + ) shouldBe SimpleEnum.ONE + + read[SimpleEnum]( + token(SimpleEnum.TWO.toString) + ) shouldBe SimpleEnum.TWO + } + + it should "derive reader for parametrized enum" in { + read[ParametrizedEnum]( + token(ParametrizedEnum.ONE.toString) + ) shouldBe ParametrizedEnum.ONE + + read[ParametrizedEnum]( + token(ParametrizedEnum.TWO.toString) + ) shouldBe ParametrizedEnum.TWO + } } diff --git a/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoWriterDerivationTest.scala b/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoWriterDerivationTest.scala index f1c1ef91..3cbbf3ea 100644 --- a/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoWriterDerivationTest.scala +++ b/modules/macro-derivation/src/test/scala-3/tethys/derivation/AutoWriterDerivationTest.scala @@ -3,7 +3,7 @@ package tethys.derivation import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec import tethys.commons.TokenNode -import tethys.commons.TokenNode.* +import tethys.commons.TokenNode.{value => token,*} import tethys.derivation.ADTWithType.* import tethys.derivation.auto.* import tethys.derivation.semiauto.* @@ -113,4 +113,14 @@ class AutoWriterDerivationTest extends AnyFlatSpec with Matchers { write(JustObject) shouldBe obj("type" -> "JustObject") write(SubChild(3)) shouldBe obj("c" -> 3) } + + it should "derive writer for simple enum" in { + SimpleEnum.ONE.asTokenList shouldBe token("ONE") + SimpleEnum.TWO.asTokenList shouldBe token("TWO") + } + + it should "derive writer for parametrized enum" in { + ParametrizedEnum.ONE.asTokenList shouldBe token("ONE") + ParametrizedEnum.TWO.asTokenList shouldBe token("TWO") + } } diff --git a/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoReaderDerivationTest.scala b/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoReaderDerivationTest.scala index 6ff7c77c..45480ffe 100644 --- a/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoReaderDerivationTest.scala +++ b/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoReaderDerivationTest.scala @@ -3,7 +3,7 @@ package tethys.derivation import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec import tethys.JsonReader -import tethys.commons.TokenNode.* +import tethys.commons.TokenNode.{value => token, *} import tethys.commons.{Token, TokenNode} import tethys.derivation.builder.{FieldStyle, ReaderBuilder, ReaderDerivationConfig} import tethys.derivation.semiauto.* @@ -323,4 +323,33 @@ class SemiautoReaderDerivationTest extends AnyFlatSpec with Matchers { )) }).getMessage shouldBe "Illegal json at '[ROOT]': unexpected field 'not_id_param', expected one of 'simple', 'id_param', 'some_param'" } + + + it should "derive reader for simple enum" in { + implicit val oneReader: JsonReader[SimpleEnum.ONE.type] = jsonReader[SimpleEnum.ONE.type] + implicit val twoReader: JsonReader[SimpleEnum.TWO.type] = jsonReader[SimpleEnum.TWO.type] + implicit val simpleEnumReader: JsonReader[SimpleEnum] = jsonReader[SimpleEnum] + + read[SimpleEnum]( + token(SimpleEnum.ONE.toString) + )shouldBe SimpleEnum.ONE + + read[SimpleEnum]( + token(SimpleEnum.TWO.toString) + ) shouldBe SimpleEnum.TWO + } + + it should "derive reader for parametrized enum" in { + implicit val oneReader: JsonReader[ParametrizedEnum.ONE.type] = jsonReader[ParametrizedEnum.ONE.type] + implicit val twoReader: JsonReader[ParametrizedEnum.TWO.type] = jsonReader[ParametrizedEnum.TWO.type] + implicit val parametrizedEnumReader: JsonReader[ParametrizedEnum] = jsonReader[ParametrizedEnum] + + read[ParametrizedEnum]( + token(ParametrizedEnum.ONE.toString) + ) shouldBe ParametrizedEnum.ONE + + read[ParametrizedEnum]( + token(ParametrizedEnum.TWO.toString) + ) shouldBe ParametrizedEnum.TWO + } } diff --git a/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoWriterDerivationTest.scala b/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoWriterDerivationTest.scala index 20e5767a..f092517e 100644 --- a/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoWriterDerivationTest.scala +++ b/modules/macro-derivation/src/test/scala-3/tethys/derivation/SemiautoWriterDerivationTest.scala @@ -6,10 +6,11 @@ import tethys.commons.TokenNode import tethys.{JsonObjectWriter, JsonWriter} import tethys.derivation.builder.{FieldStyle, WriterBuilder, WriterDerivationConfig} import tethys.writers.tokens.SimpleTokenWriter.* -import tethys.commons.TokenNode.* +import tethys.commons.TokenNode.{value as token, *} import tethys.derivation.ADTWithType.{ADTWithTypeA, ADTWithTypeB} import tethys.derivation.semiauto.* import tethys.writers.instances.SimpleJsonObjectWriter +import tethys.writers.tokens.SimpleTokenWriter class SemiautoWriterDerivationTest extends AnyFlatSpec with Matchers { @@ -190,4 +191,44 @@ class SemiautoWriterDerivationTest extends AnyFlatSpec with Matchers { write(JustObject) shouldBe obj("__type" -> "JustObject") write(SubChild(3)) shouldBe obj("c" -> 3, "__type" -> "SubChild") } + + it should "derive writer for simple enum" in { + implicit val oneWriter: JsonObjectWriter[SimpleEnum.ONE.type] = jsonWriter[SimpleEnum.ONE.type] + implicit val twoWriter: JsonObjectWriter[SimpleEnum.TWO.type] = jsonWriter[SimpleEnum.TWO.type] + implicit val simpleEnumWriter: JsonWriter[SimpleEnum] = jsonWriter[SimpleEnum] + + SimpleEnum.ONE.asTokenList shouldBe token("ONE") + SimpleEnum.TWO.asTokenList shouldBe token("TWO") + } + + it should "derive writer for parametrized enum" in { + implicit val oneWriter: JsonObjectWriter[ParametrizedEnum.ONE.type] = jsonWriter[ParametrizedEnum.ONE.type] + implicit val twoWriter: JsonObjectWriter[ParametrizedEnum.TWO.type] = jsonWriter[ParametrizedEnum.TWO.type] + implicit val parametrizedEnumWriter: JsonWriter[ParametrizedEnum] = jsonWriter[ParametrizedEnum] + + ParametrizedEnum.ONE.asTokenList shouldBe token("ONE") + ParametrizedEnum.TWO.asTokenList shouldBe token("TWO") + } + + it should "derive writer with discriminator for simple enum" in { + implicit val oneWriter: JsonObjectWriter[SimpleEnum.ONE.type] = jsonWriter[SimpleEnum.ONE.type] + implicit val twoWriter: JsonObjectWriter[SimpleEnum.TWO.type] = jsonWriter[SimpleEnum.TWO.type] + implicit val simpleEnumWriter: JsonWriter[SimpleEnum] = jsonWriter[SimpleEnum]( + WriterDerivationConfig.empty.withDiscriminator("__type") + ) + + SimpleEnum.ONE.asTokenList shouldBe obj("__type" -> "ONE") + SimpleEnum.TWO.asTokenList shouldBe obj("__type" -> "TWO") + } + + it should "derive writer with discriminator for parametrized enum" in { + implicit val oneWriter: JsonObjectWriter[ParametrizedEnum.ONE.type] = jsonWriter[ParametrizedEnum.ONE.type] + implicit val twoWriter: JsonObjectWriter[ParametrizedEnum.TWO.type] = jsonWriter[ParametrizedEnum.TWO.type] + implicit val simpleEnumWriter: JsonWriter[ParametrizedEnum] = jsonWriter[ParametrizedEnum]( + WriterDerivationConfig.empty.withDiscriminator("__type") + ) + + ParametrizedEnum.ONE.asTokenList shouldBe obj ("__type" -> "ONE") + ParametrizedEnum.TWO.asTokenList shouldBe obj ("__type" -> "TWO") + } } diff --git a/modules/macro-derivation/src/test/scala-3/tethys/derivation/package.scala b/modules/macro-derivation/src/test/scala-3/tethys/derivation/package.scala index c2852a3c..5a5068b2 100644 --- a/modules/macro-derivation/src/test/scala-3/tethys/derivation/package.scala +++ b/modules/macro-derivation/src/test/scala-3/tethys/derivation/package.scala @@ -24,4 +24,13 @@ package object derivation { case class SeqMaster4(a: Seq[Int]) case class CamelCaseNames(someParam: Int, IDParam: Int, simple: Int) + + enum SimpleEnum { + case ONE, TWO + } + + enum ParametrizedEnum(val i: Int) { + case ONE extends ParametrizedEnum(1) + case TWO extends ParametrizedEnum(2) + } }