From d24ae5f83bf3b479945785773a1d426a1f2d57bc Mon Sep 17 00:00:00 2001 From: Andriy Plokhotnyuk Date: Thu, 9 Jan 2025 14:00:56 +0100 Subject: [PATCH] Fix using of fieldName annotations to rename fields (not aliasing) (#773) --- .../main/scala-3/zio/schema/DeriveSchema.scala | 8 ++++---- .../test/scala/zio/schema/DeriveSchemaSpec.scala | 15 ++++++++++++--- .../shared/src/main/scala/zio/schema/Schema.scala | 14 ++++++++------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/zio-schema-derivation/shared/src/main/scala-3/zio/schema/DeriveSchema.scala b/zio-schema-derivation/shared/src/main/scala-3/zio/schema/DeriveSchema.scala index 47b4ac5a6..78e3e9c4c 100644 --- a/zio-schema-derivation/shared/src/main/scala-3/zio/schema/DeriveSchema.scala +++ b/zio-schema-derivation/shared/src/main/scala-3/zio/schema/DeriveSchema.scala @@ -256,7 +256,7 @@ private case class DeriveSchema()(using val ctx: Quotes) { tpe.asType } val annotations = paramAnns.getOrElse(label, List.empty) - val nameExpr = annotations.collectFirst { + val nameExpr = annotations.reverse.collectFirst { case ann if ann.isExprOf[fieldName] => val fieldNameAnn = ann.asExprOf[fieldName] '{${fieldNameAnn}.name} @@ -291,7 +291,7 @@ private case class DeriveSchema()(using val ctx: Quotes) { tpe.asType } val annotations = paramAnns.getOrElse(label, List.empty) - val nameExpr = annotations.collectFirst { + val nameExpr = annotations.reverse.collectFirst { case ann if ann.isExprOf[fieldName] => val fieldNameAnn = ann.asExprOf[fieldName] '{${fieldNameAnn}.name} @@ -478,7 +478,7 @@ private case class DeriveSchema()(using val ctx: Quotes) { val chunk = '{ zio.Chunk.fromIterable(${ Expr.ofSeq(anns.reverse) }) } if (anns.nonEmpty) { - val (newName, newNameValue) = anns.collectFirst { + val (newName, newNameValue) = anns.reverse.collectFirst { case ann if ann.isExprOf[fieldName] => val fieldNameAnn = ann.asExprOf[fieldName] ('{${fieldNameAnn}.name}, extractFieldNameValue(fieldNameAnn)) @@ -529,7 +529,7 @@ private case class DeriveSchema()(using val ctx: Quotes) { val chunk = '{ zio.Chunk.fromIterable(${ Expr.ofSeq(anns.reverse) }) } if (anns.nonEmpty) { - val newName = anns.collectFirst { + val newName = anns.reverse.collectFirst { case ann if ann.isExprOf[fieldName] => '{${ann.asExprOf[fieldName]}.name} }.getOrElse(Expr(name)) diff --git a/zio-schema-derivation/shared/src/test/scala/zio/schema/DeriveSchemaSpec.scala b/zio-schema-derivation/shared/src/test/scala/zio/schema/DeriveSchemaSpec.scala index 9ce237b50..a6ece21ad 100644 --- a/zio-schema-derivation/shared/src/test/scala/zio/schema/DeriveSchemaSpec.scala +++ b/zio-schema-derivation/shared/src/test/scala/zio/schema/DeriveSchemaSpec.scala @@ -235,7 +235,10 @@ object DeriveSchemaSpec extends ZIOSpecDefault with VersionSpecificDeriveSchemaS implicit lazy val schema: Schema.EnumN[Enum23, CaseSet.Aux[Enum23]] = DeriveSchema.gen[Enum23] } - case class RenamedField(@fieldName("renamed") name: String, @fieldName("number") num: Int) + case class RenamedField( + @fieldName("renamed") @fieldName("ignored_rename") name: String, + @fieldName("number") num: Int + ) object RenamedField { implicit lazy val schema: Schema[RenamedField] = DeriveSchema.gen[RenamedField] @@ -445,7 +448,9 @@ object DeriveSchemaSpec extends ZIOSpecDefault with VersionSpecificDeriveSchemaS assert(Schema[ContainsSchema].toString)(not(containsString("null")) && not(equalTo("$Lazy$"))) }, test("correctly derives renaming field when fieldName annotation is present") { - val derived = DeriveSchema.gen[RenamedField] + val derived = DeriveSchema.gen[RenamedField] + val derivedField1 = derived.asInstanceOf[Schema.CaseClass2[String, Int, RenamedField]].field1 + val derivedField2 = derived.asInstanceOf[Schema.CaseClass2[String, Int, RenamedField]].field2 val expected: Schema[RenamedField] = { Schema.CaseClass2( @@ -453,7 +458,7 @@ object DeriveSchemaSpec extends ZIOSpecDefault with VersionSpecificDeriveSchemaS field01 = Schema.Field( "renamed", Schema.Primitive(StandardType.StringType), - Chunk(fieldName("renamed")), + Chunk(fieldName("renamed"), fieldName("ignored_rename")), get0 = _.name, set0 = (a, b: String) => a.copy(name = b) ), @@ -467,6 +472,10 @@ object DeriveSchemaSpec extends ZIOSpecDefault with VersionSpecificDeriveSchemaS RenamedField.apply ) } + assert(derivedField1.fieldName)(equalTo("renamed")) && + assert(derivedField1.nameAndAliases)(equalTo(Set("renamed"))) && + assert(derivedField2.fieldName)(equalTo("number")) && + assert(derivedField2.nameAndAliases)(equalTo(Set("number"))) && assert(derived)(hasSameSchema(expected)) && assert(verifyFieldName[derived.Field1]("renamed"))(isTrue) }, diff --git a/zio-schema/shared/src/main/scala/zio/schema/Schema.scala b/zio-schema/shared/src/main/scala/zio/schema/Schema.scala index a185d9297..a41b8540c 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/Schema.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/Schema.scala @@ -432,16 +432,18 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality { val transient: Boolean = annotations.exists(_.isInstanceOf[transientField]) - val nameAndAliases: scala.collection.immutable.Set[String] = - annotations.collect { - case aliases: fieldNameAliases => aliases.aliases - case f: fieldName => Seq(f.name) - }.flatten.toSet + name - val fieldName: String = annotations.collectFirst { case f: fieldName => f.name }.getOrElse(name) + val nameAndAliases: scala.collection.immutable.Set[String] = + annotations.foldLeft(scala.collection.immutable.Set(fieldName)) { (acc, annotation) => + annotation match { + case aliases: fieldNameAliases => acc ++ aliases.aliases + case _ => acc + } + } + override def toString: String = s"Field($name,$schema)" }