From a28bb37d67f992b739c1c56b0be96e654732632a Mon Sep 17 00:00:00 2001 From: Erik van Oosten Date: Mon, 9 May 2022 20:16:24 +0200 Subject: [PATCH] Add type class to derived record name This approach has the advantage of giving each type instance a different name. This is relevant if the consumer of the schema uses code generation. The feature is copied from Avro4s. --- .../src/main/scala-2/vulcan/generic/package.scala | 5 ++++- .../src/main/scala-3/vulcan/generic/package.scala | 9 +++++++-- .../vulcan/generic/GenericDerivationCodecSpec.scala | 12 ++++++++++++ .../vulcan/generic/examples/CaseClassTypeClass.scala | 10 ++++++++++ .../examples/CaseClassTypeClassAvroName.scala | 11 +++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClass.scala create mode 100644 modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClassAvroName.scala diff --git a/modules/generic/src/main/scala-2/vulcan/generic/package.scala b/modules/generic/src/main/scala-2/vulcan/generic/package.scala index 34c6f166..7aa2402f 100644 --- a/modules/generic/src/main/scala-2/vulcan/generic/package.scala +++ b/modules/generic/src/main/scala-2/vulcan/generic/package.scala @@ -56,12 +56,15 @@ package object generic { val param = caseClass.parameters.head param.typeclass.imap(value => caseClass.rawConstruct(List(value)))(param.dereference) } else { + def recursiveShortNames(typeName: TypeName): Seq[String] = { + typeName.short +: typeName.typeArguments.flatMap(recursiveShortNames) + } Codec .record[A]( name = caseClass.annotations .collectFirst { case AvroName(namespace) => namespace } - .getOrElse(caseClass.typeName.short), + .getOrElse(recursiveShortNames(caseClass.typeName).mkString("__")), namespace = caseClass.annotations .collectFirst { case AvroNamespace(namespace) => namespace } .getOrElse(caseClass.typeName.owner), diff --git a/modules/generic/src/main/scala-3/vulcan/generic/package.scala b/modules/generic/src/main/scala-3/vulcan/generic/package.scala index 90b538b5..53e7d7fb 100644 --- a/modules/generic/src/main/scala-3/vulcan/generic/package.scala +++ b/modules/generic/src/main/scala-3/vulcan/generic/package.scala @@ -28,12 +28,16 @@ package object generic { ) extends Derivation[Codec] { inline def derive[A](using Mirror.Of[A]): Codec[A] = derived[A] - final def join[A](caseClass: CaseClass[Codec, A]): Codec[A] = + final def join[A](caseClass: CaseClass[Codec, A]): Codec[A] = { + def recursiveShortNames(typeInfo: TypeInfo): Seq[String] = { + typeInfo.short +: typeInfo.typeParams.toSeq.flatMap(recursiveShortNames) + } + Codec .record[A]( name = caseClass.annotations .collectFirst { case AvroName(namespace) => namespace } - .getOrElse(caseClass.typeInfo.short), + .getOrElse(recursiveShortNames(caseClass.typeInfo).mkString("__")), namespace = caseClass.annotations .collectFirst { case AvroNamespace(namespace) => namespace } .getOrElse(caseClass.typeInfo.owner), @@ -68,6 +72,7 @@ package object generic { } .map(caseClass.rawConstruct(_)) } + } final def split[A](sealedTrait: SealedTrait[Codec, A]): Codec.Aux[Any, A] = { Codec diff --git a/modules/generic/src/test/scala/vulcan/generic/GenericDerivationCodecSpec.scala b/modules/generic/src/test/scala/vulcan/generic/GenericDerivationCodecSpec.scala index b0505e24..952e794c 100644 --- a/modules/generic/src/test/scala/vulcan/generic/GenericDerivationCodecSpec.scala +++ b/modules/generic/src/test/scala/vulcan/generic/GenericDerivationCodecSpec.scala @@ -69,6 +69,18 @@ final class GenericDerivationCodecSpec extends CodecBase { } } + it("should append types classes in record name") { + assertSchemaIs[CaseClassTypeClass[String]] { + """{"type":"record","name":"CaseClassTypeClass__String","namespace":"vulcan.generic.examples","fields":[{"name":"value","type":["null","string"]}]}""" + } + } + + it("should ignore type class names with annotation for record name") { + assertSchemaIs[CaseClassTypeClassAvroName[String]] { + """{"type":"record","name":"CaseClassOtherName","namespace":"vulcan.generic.examples","fields":[{"name":"value","type":["null","string"]}]}""" + } + } + } describe("encode") { diff --git a/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClass.scala b/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClass.scala new file mode 100644 index 00000000..c93a34c7 --- /dev/null +++ b/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClass.scala @@ -0,0 +1,10 @@ +package vulcan.generic.examples + +import vulcan.Codec +import vulcan.generic._ + +final case class CaseClassTypeClass[A](value: Option[A]) + +object CaseClassTypeClass { + implicit val codecString: Codec[CaseClassTypeClass[String]] = Codec.derive +} diff --git a/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClassAvroName.scala b/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClassAvroName.scala new file mode 100644 index 00000000..8e00f46f --- /dev/null +++ b/modules/generic/src/test/scala/vulcan/generic/examples/CaseClassTypeClassAvroName.scala @@ -0,0 +1,11 @@ +package vulcan.generic.examples + +import vulcan.Codec +import vulcan.generic._ + +@AvroName("CaseClassOtherName") +final case class CaseClassTypeClassAvroName[A](value: Option[A]) + +object CaseClassTypeClassAvroName { + implicit val codecString: Codec[CaseClassTypeClassAvroName[String]] = Codec.derive +}