Skip to content

Commit

Permalink
Add tests for scala3 enum derivation and fix writer derivation for sc…
Browse files Browse the repository at this point in the history
…ala3 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 <a.m.petukhov@tinkoff.ru>
  • Loading branch information
Alexxand and Aleksandr Petukhov authored Jul 17, 2023
1 parent ade5b16 commit 3059341
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -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")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit 3059341

Please sign in to comment.