Skip to content

Commit 01c3dc7

Browse files
committed
Fix support of named tuples for different combination of tuple kinds when defining names and value types
1 parent 80a8447 commit 01c3dc7

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ lazy val `jsoniter-scala-next-tests` = crossProject(JVMPlatform, JSPlatform, Nat
215215
.settings(commonSettings)
216216
.settings(noPublishSettings)
217217
.settings(
218-
crossScalaVersions := Seq("3.7.3-RC1", "2.13.16"),
218+
crossScalaVersions := Seq("3.7.3-RC1"),
219219
libraryDependencies ++= Seq(
220220
"org.scalatestplus" %%% "scalacheck-1-18" % "3.2.19.0" % Test,
221221
"org.scalatest" %%% "scalatest" % "3.2.19" % Test

jsoniter-scala-macros/shared/src/main/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ object JsonCodecMaker {
980980
def genNew(argss: List[List[Term]]): Term
981981
}
982982

983-
case class NamedTupleInfo(tpe: TypeRepr, tupleType: Type[?],
983+
case class NamedTupleInfo(tpe: TypeRepr, tupleType: Type[?], isGeneric: Boolean,
984984
override val paramLists: List[List[FieldInfo]]) extends TypeInfo(tpe, paramLists) {
985985
def genNew(argss: List[List[Term]]): Term =
986986
// Borrowed from an amazing work of Aleksander Rainko: https://github.com/arainko/ducktape/blob/8d779f0303c23fd45815d3574467ffc321a8db2b/ducktape/src/main/scala/io/github/arainko/ducktape/internal/ProductConstructor.scala#L22
@@ -991,16 +991,20 @@ object JsonCodecMaker {
991991

992992
def getNamedTupleInfo(tpe: TypeRepr): NamedTupleInfo = namedTupleInfos.getOrElseUpdate(tpe, {
993993
tpe match {
994-
case AppliedType(ntTpe, List(nTpe@AppliedType(_, nameConstants), tTpe)) if ntTpe.dealias.typeSymbol.fullName == "scala.NamedTuple$.NamedTuple" =>
995-
val fields =
996-
if (isGenericTuple(tTpe)) namedGenericTupleNames(nTpe).zip(genericTupleTypeArgs(tTpe))
997-
else nameConstants.collect { case ConstantType(StringConstant(x)) => x}.zip(typeArgs(tTpe))
994+
case AppliedType(_, List(nTpe @ AppliedType(_, nameConstants), tTpe)) =>
995+
val names =
996+
if (isGenericTuple(nTpe)) namedGenericTupleNames(nTpe)
997+
else nameConstants.collect { case ConstantType(StringConstant(x)) => x }
998+
val isGeneric = isGenericTuple(tTpe)
999+
val types =
1000+
if (isGeneric) genericTupleTypeArgs(tTpe)
1001+
else typeArgs(tTpe)
9981002
val noSymbol = Symbol.noSymbol
9991003
var i = - 1
1000-
NamedTupleInfo(tpe, tTpe.asType, List(fields.map { case (name, fTpe) =>
1004+
NamedTupleInfo(tpe, tTpe.asType, isGeneric, List(names.zip(types).map { case (name, fTpe) =>
10011005
i += 1
1002-
val mappedName = cfg.fieldNameMapper (name).getOrElse (name)
1003-
FieldInfo (noSymbol, mappedName, noSymbol, None, fTpe, false, false, i)
1006+
val mappedName = cfg.fieldNameMapper(name).getOrElse(name)
1007+
FieldInfo(noSymbol, mappedName, noSymbol, None, fTpe, false, false, i)
10041008
}))
10051009
case _ => fail(s"Unexpected named type: ${tpe.show}")
10061010
}
@@ -2726,9 +2730,7 @@ object JsonCodecMaker {
27262730
optDiscriminator: Option[WriteDiscriminator],
27272731
out: Expr[JsonWriter])(using Quotes): Expr[Unit] =
27282732
val tpe = types.head
2729-
var i = -1
27302733
val writeFields = typeInfo.fields.map { fieldInfo =>
2731-
i += 1
27322734
val fDefault =
27332735
if (cfg.transientDefault) fieldInfo.defaultValue
27342736
else None
@@ -2737,9 +2739,11 @@ object JsonCodecMaker {
27372739
case '[ft] =>
27382740
val getter = typeInfo match {
27392741
case namedTupleInfo: NamedTupleInfo =>
2740-
if (typeInfo.fields.size > 22) '{ $x.asInstanceOf[Tuple].productElement(${Expr(i)}).asInstanceOf[ft] }.asTerm
2741-
else namedTupleInfo.tupleType match {
2742-
case '[tt] => Select.unique('{ $x.asInstanceOf[tt] }.asTerm, s"_${i + 1}")
2742+
val idx = fieldInfo.nonTransientFieldIndex
2743+
if (namedTupleInfo.isGeneric) {
2744+
'{ $x.asInstanceOf[Tuple].productElement(${Expr(idx)}).asInstanceOf[ft] }.asTerm
2745+
} else namedTupleInfo.tupleType match {
2746+
case '[tt] => Select.unique('{ $x.asInstanceOf[tt] }.asTerm, s"_${idx + 1}")
27432747
}
27442748
case _ => Select(x.asTerm, fieldInfo.getterOrField)
27452749
}

jsoniter-scala-next-tests/shared/src/test/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMakerNamedTupleSpec.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,24 @@ class JsonCodecMakerNamedTupleSpec extends VerifyingSpec {
88
"serialize and deserialize Scala 3 named tuples" in {
99
verifySerDeser(make[(i: Int, s: String)], (i = 1, s = "VVV"), """{"i":1,"s":"VVV"}""")
1010
}
11+
"serialize and deserialize Scala 3 named tuples with generic tuple for names" in {
12+
verifySerDeser(make[NamedTuple.NamedTuple["i" *: "s" *: EmptyTuple, (Int, String)]],
13+
(i = 1, s = "VVV"), """{"i":1,"s":"VVV"}""")
14+
}
15+
"serialize and deserialize Scala 3 named tuples with generic tuple for value types" in {
16+
verifySerDeser(make[NamedTuple.NamedTuple[("i", "s"), Int *: String *: EmptyTuple]],
17+
(i = 1, s = "VVV"), """{"i":1,"s":"VVV"}""")
18+
}
1119
"serialize and deserialize generic Scala 3 named tuples" in {
12-
type GenericNamedTuple[A, B] = (i: A, s: B)
20+
type GenericNamedTuple[A, B] = (a: A, b: B)
21+
22+
verifySerDeser(make[GenericNamedTuple[Option[Int], List[String]]], (a = Some(1), b = List("VVV")),
23+
"""{"a":1,"b":["VVV"]}""")
24+
}
25+
"serialize and deserialize higher-kind Scala 3 named tuples" in {
26+
type HKNamedTuple[F[_], G[_]] = (i: F[Int], s: G[String])
1327

14-
verifySerDeser(make[GenericNamedTuple[Option[Int], List[String]]], (i = Some(1), s = List("VVV")),
28+
verifySerDeser(make[HKNamedTuple[Option, List]], (i = Some(1), s = List("VVV")),
1529
"""{"i":1,"s":["VVV"]}""")
1630
}
1731
"serialize and deserialize nested Scala 3 named tuples" in {

0 commit comments

Comments
 (0)