diff --git a/build.sbt b/build.sbt index b2096ce7..17340887 100644 --- a/build.sbt +++ b/build.sbt @@ -80,7 +80,6 @@ lazy val generic = project if (scalaVersion.value.startsWith("2")) Seq( "com.propensive" %% "magnolia" % magnolia2Version, - "com.chuusai" %% "shapeless" % shapelessVersion, "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided ) else diff --git a/modules/enumeratum/src/main/scala/enumeratum/Vulcan.scala b/modules/enumeratum/src/main/scala-2/enumeratum/Vulcan.scala similarity index 100% rename from modules/enumeratum/src/main/scala/enumeratum/Vulcan.scala rename to modules/enumeratum/src/main/scala-2/enumeratum/Vulcan.scala diff --git a/modules/enumeratum/src/main/scala/enumeratum/VulcanEnum.scala b/modules/enumeratum/src/main/scala-2/enumeratum/VulcanEnum.scala similarity index 100% rename from modules/enumeratum/src/main/scala/enumeratum/VulcanEnum.scala rename to modules/enumeratum/src/main/scala-2/enumeratum/VulcanEnum.scala diff --git a/modules/enumeratum/src/main/scala/enumeratum/values/Vulcan.scala b/modules/enumeratum/src/main/scala-2/enumeratum/values/Vulcan.scala similarity index 100% rename from modules/enumeratum/src/main/scala/enumeratum/values/Vulcan.scala rename to modules/enumeratum/src/main/scala-2/enumeratum/values/Vulcan.scala diff --git a/modules/enumeratum/src/main/scala/enumeratum/values/VulcanValueEnum.scala b/modules/enumeratum/src/main/scala-2/enumeratum/values/VulcanValueEnum.scala similarity index 100% rename from modules/enumeratum/src/main/scala/enumeratum/values/VulcanValueEnum.scala rename to modules/enumeratum/src/main/scala-2/enumeratum/values/VulcanValueEnum.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 09274bc2..7b0ec3f9 100644 --- a/modules/generic/src/main/scala-2/vulcan/generic/package.scala +++ b/modules/generic/src/main/scala-2/vulcan/generic/package.scala @@ -6,48 +6,16 @@ package vulcan -import scala.language.experimental.macros -import scala.reflect.runtime.universe.WeakTypeTag +import cats.data.Chain +import cats.free.FreeApplicative import cats.implicits._ import magnolia._ -import shapeless.{:+:, CNil, Coproduct, Inl, Inr, Lazy} -import shapeless.ops.coproduct.{Inject, Selector} import vulcan.internal.tags._ -import cats.data.Chain -import cats.free.FreeApplicative -package object generic { - implicit final val cnilCodec: Codec.Aux[Nothing, CNil] = - Codec.UnionCodec(Chain.empty).asInstanceOf[Codec.Aux[Nothing, CNil]].withTypeName("Coproduct") - - implicit final def coproductCodec[H, T <: Coproduct]( - implicit headCodec: Codec[H], - tailCodec: Lazy[Codec[T]] - ): Codec[H :+: T] = - tailCodec.value match { - case Codec.WithTypeName(Codec.Validated(u: Codec.UnionCodec[T], _), typeName) => - val tailAlts: Chain[Codec.Alt[H :+: T]] = - u.alts.map(_.imap[H :+: T](_.eliminate(_ => None, Some(_)), Inr(_))) - Codec - .UnionCodec( - tailAlts - .prepend( - Codec.Alt(headCodec, Prism.instance[H :+: T, H](_.select)(Inl(_))) - ) - ) - .withTypeName(typeName) - case Codec.Fail(error) => Codec.Fail(error) - case other => - throw new IllegalArgumentException( - s"cannot derive coproduct codec from non-union ${other.getClass()}" - ) - } +import scala.language.experimental.macros +import scala.reflect.runtime.universe.WeakTypeTag - implicit final def coproductPrism[C <: Coproduct, A]( - implicit inject: Inject[C, A], - selector: Selector[C, A] - ): Prism[C, A] = - Prism.instance(selector(_))(inject(_)) +package object generic { implicit final class MagnoliaCodec private[generic] ( private val codec: Codec.type 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 2649d3aa..3390108a 100644 --- a/modules/generic/src/main/scala-3/vulcan/generic/package.scala +++ b/modules/generic/src/main/scala-3/vulcan/generic/package.scala @@ -7,19 +7,19 @@ package vulcan -import org.apache.avro.generic._ +import org.apache.avro.generic.* import org.apache.avro.Schema import shapeless3.deriving._ -import scala.compiletime._ +import scala.compiletime.* import scala.reflect.ClassTag import scala.deriving.Mirror import cats.data.Chain -import cats.implicits._ +import cats.implicits.* import cats.free.FreeApplicative -import magnolia1._ -import org.apache.avro.generic._ +import magnolia1.* +import org.apache.avro.generic.* import org.apache.avro.Schema -import vulcan.internal.converters.collection._ +import vulcan.internal.converters.collection.* package object generic { diff --git a/modules/generic/src/test/scala-2/vulcan/generic/CoproductCodecSpec.scala b/modules/generic/src/test/scala-2/vulcan/generic/CoproductCodecSpec.scala deleted file mode 100644 index be286c6d..00000000 --- a/modules/generic/src/test/scala-2/vulcan/generic/CoproductCodecSpec.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2019-2024 OVO Energy Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package vulcan.generic - -import cats.syntax.all._ -import org.apache.avro.Schema -import shapeless.{:+:, CNil, Coproduct} -import vulcan._ -import vulcan.generic.examples._ - -final class CoproductCodecSpec extends CodecBase { - describe("Codec") { - describe("cnil") { - describe("schema") { - it("should be encoded as empty union") { - assertSchemaIs[CNil] { - """[]""" - } - } - } - - describe("encode") { - it("should error") { - assertEncodeError[CNil]( - null, - "Error encoding Coproduct: Exhausted alternatives for type null" - ) - } - } - - describe("decode") { - it("should error") { - assertDecodeError[CNil]( - null, - unsafeSchema[CNil], - "Error decoding Coproduct: Exhausted alternatives for type null" - ) - } - } - } - - describe("coproduct") { - describe("schema") { - it("should be encoded as union") { - assertSchemaIs[Int :+: String :+: CNil] { - """["int","string"]""" - } - } - - it("should capture errors on nested unions") { - assertSchemaError[Int :+: Option[String] :+: CNil] { - """org.apache.avro.AvroRuntimeException: Nested union: [["null","string"]]""" - } - } - - it("should fail if CNil schema is not union") { - val result = Either.catchNonFatal { - coproductCodec[Int, CNil]( - Codec.int, - shapeless.Lazy { - Codec.unit.asInstanceOf[Codec.Aux[Null, CNil]] - } - ) - } - - assert(result.swap.value.isInstanceOf[IllegalArgumentException]) - - } - } - - describe("encode") { - it("should encode first in coproduct using first type") { - type A = Int :+: String :+: CNil - assertEncodeIs[A]( - Coproduct[A](123), - Right(unsafeEncode(123)) - ) - } - - it("should encode second in coproduct using second type") { - type A = Int :+: String :+: CNil - assertEncodeIs[A]( - Coproduct[A]("abc"), - Right(unsafeEncode("abc")) - ) - } - } - - describe("decode") { - it("should error if schema is not in union") { - type A = Int :+: String :+: CNil - assertDecodeError[A]( - unsafeEncode(Coproduct[A](123)), - unsafeSchema[String], - "Error decoding Coproduct: Exhausted alternatives for type java.lang.Integer" - ) - } - - it("should decode if schema is part of union") { - type A = Int :+: String :+: CNil - assertDecodeIs[A]( - unsafeEncode(Coproduct[A](123)), - Right(Coproduct[A](123)), - Some(unsafeSchema[Int]) - ) - } - - it("should error on empty union schema") { - type A = Int :+: String :+: CNil - assertDecodeError[A]( - unsafeEncode(Coproduct[A](123)), - Schema.createUnion(), - "Error decoding Coproduct: Exhausted alternatives for type java.lang.Integer" - ) - } - - it("should decode first in coproduct using first type") { - type A = Int :+: String :+: CNil - assertDecodeIs[A]( - unsafeEncode(Coproduct[A](123)), - Right(Coproduct[A](123)) - ) - } - - it("should decode second in coproduct using second type") { - type A = Int :+: String :+: CNil - assertDecodeIs[A]( - unsafeEncode(Coproduct[A]("abc")), - Right(Coproduct[A]("abc")) - ) - } - - it("should decode coproduct with records") { - type A = CaseClassField :+: CaseClassTwoFields :+: Int :+: CNil - assertDecodeIs[A]( - unsafeEncode(Coproduct[A](CaseClassField(10))), - Right(Coproduct[A](CaseClassField(10))) - ) - - assertDecodeIs[A]( - unsafeEncode(Coproduct[A](CaseClassTwoFields("name", 10))), - Right(Coproduct[A](CaseClassTwoFields("name", 10))) - ) - - assertDecodeIs[A]( - unsafeEncode(Coproduct[A](123)), - Right(Coproduct[A](123)) - ) - } - - it("should error if no schema in union with container name") { - type A = Int :+: CaseClassField :+: CNil - assertDecodeError[A]( - unsafeEncode(Coproduct[A](CaseClassField(10))), - unsafeSchema[Int :+: String :+: CNil], - "Error decoding Coproduct: Missing schema CaseClassField in union" - ) - } - - it("should error when not enough union schemas") { - type A = Int :+: String :+: CNil - assertDecodeError[A]( - unsafeEncode(Coproduct[A]("abc")), - Schema.createUnion(), - "Error decoding Coproduct: Exhausted alternatives for type org.apache.avro.util.Utf8" - ) - } - - it("should error when not enough union schemas when decoding record") { - type A = Int :+: CaseClassField :+: CNil - assertDecodeError[A]( - unsafeEncode(Coproduct[A](CaseClassField(10))), - unsafeSchema[CNil], - "Error decoding Coproduct: Missing schema CaseClassField in union" - ) - } - } - } - } -} diff --git a/modules/generic/src/test/scala-2/vulcan/generic/CoproductRoundtripSpec.scala b/modules/generic/src/test/scala-2/vulcan/generic/CoproductRoundtripSpec.scala deleted file mode 100644 index 4578a285..00000000 --- a/modules/generic/src/test/scala-2/vulcan/generic/CoproductRoundtripSpec.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019-2024 OVO Energy Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package vulcan.generic - -import cats.Eq -import cats.implicits._ -import org.scalacheck.{Arbitrary, Gen} -import org.scalacheck.Arbitrary.arbitrary -import shapeless.{:+:, CNil, Coproduct} -import vulcan._ -import vulcan.generic.examples._ - -final class CoproductRoundtripSpec extends RoundtripBase { - describe("coproduct") { - type Types = CaseClassField :+: Int :+: CaseClassAvroDoc :+: CNil - - implicit val arbitraryTypes: Arbitrary[Types] = - Arbitrary { - Gen.oneOf( - arbitrary[Int].map(n => Coproduct[Types](CaseClassField(n))), - arbitrary[Int].map(n => Coproduct[Types](n)), - arbitrary[Option[String]].map(os => Coproduct[Types](CaseClassAvroDoc(os))) - ) - } - - implicit val eqTypes: Eq[Types] = - Eq.fromUniversalEquals - - it("roundtrip.derived") { - roundtrip[Types] - } - - it("roundtrip.union") { - implicit val codec: Codec[Types] = - Codec.union { alt => - alt[CaseClassField] |+| alt[Int] |+| alt[CaseClassAvroDoc] - } - - roundtrip[Types] - } - } -}