diff --git a/README.md b/README.md index d7c461505..6ec459551 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,12 @@ _ZIO Schema_ is used by a growing number of ZIO libraries, including _ZIO Flow_, In order to use this library, we need to add the following lines in our `build.sbt` file: ```scala -libraryDependencies += "dev.zio" %% "zio-schema" % "0.4.6" -libraryDependencies += "dev.zio" %% "zio-schema-json" % "0.4.6" -libraryDependencies += "dev.zio" %% "zio-schema-protobuf" % "0.4.6" +libraryDependencies += "dev.zio" %% "zio-schema" % "0.4.7" +libraryDependencies += "dev.zio" %% "zio-schema-json" % "0.4.7" +libraryDependencies += "dev.zio" %% "zio-schema-protobuf" % "0.4.7" // Required for automatic generic derivation of schemas -libraryDependencies += "dev.zio" %% "zio-schema-derivation" % "0.4.6", +libraryDependencies += "dev.zio" %% "zio-schema-derivation" % "0.4.7", libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided" ``` diff --git a/zio-schema-derivation/shared/src/main/scala-2/zio/schema/Derive.scala b/zio-schema-derivation/shared/src/main/scala-2/zio/schema/Derive.scala index 6181419a2..fcdc372e4 100644 --- a/zio-schema-derivation/shared/src/main/scala-2/zio/schema/Derive.scala +++ b/zio-schema-derivation/shared/src/main/scala-2/zio/schema/Derive.scala @@ -294,7 +294,7 @@ object Derive { } else if (tpe <:< tuple22Tpe) { tupleN() } else if (isCaseObject(tpe)) { - q"$deriver.deriveRecord[$tpe]($forcedSchema.asInstanceOf[_root_.zio.schema.Schema.Record[$tpe]], _root_.zio.Chunk.empty, $summoned)" + q"$deriver.tryDeriveRecord[$tpe]($forcedSchema.asInstanceOf[_root_.zio.schema.Schema[$tpe]], _root_.zio.Chunk.empty, $summoned)" } else if (isCaseClass(tpe)) { val fields = tpe.decls.sorted.collect { case p: TermSymbol if p.isCaseAccessor && !p.isMethod => p @@ -334,7 +334,7 @@ object Derive { q"""{ lazy val $schemaRef = $forcedSchema.asInstanceOf[_root_.zio.schema.Schema.Record[$tpe]] - lazy val $selfRefWithType = $deriver.deriveRecord[$tpe]($schemaRef, _root_.zio.Chunk(..$fieldInstances), $summoned) + lazy val $selfRefWithType = $deriver.tryDeriveRecord[$tpe]($forcedSchema.asInstanceOf[_root_.zio.schema.Schema[$tpe]], _root_.zio.Chunk(..$fieldInstances), $summoned) $selfRef }""" } @@ -350,7 +350,7 @@ object Derive { } q"""{ lazy val $schemaRef = $forcedSchema.asInstanceOf[_root_.zio.schema.Schema.Enum[$tpe]] - lazy val $selfRefWithType = $deriver.deriveEnum[$tpe]($schemaRef, _root_.zio.Chunk(..$subtypeInstances), $summoned) + lazy val $selfRefWithType = $deriver.tryDeriveEnum[$tpe]($forcedSchema.asInstanceOf[_root_.zio.schema.Schema[$tpe]], _root_.zio.Chunk(..$subtypeInstances), $summoned) $selfRef }""" } else if (isMap(tpe)) { diff --git a/zio-schema-derivation/shared/src/main/scala-3/zio/schema/Derive.scala b/zio-schema-derivation/shared/src/main/scala-3/zio/schema/Derive.scala index eaa0fc395..17b8fd15f 100644 --- a/zio-schema-derivation/shared/src/main/scala-3/zio/schema/Derive.scala +++ b/zio-schema-derivation/shared/src/main/scala-3/zio/schema/Derive.scala @@ -349,12 +349,18 @@ private case class DeriveInstance()(using val ctx: Quotes) extends ReflectionUti result } - def deriveCaseObject[F[_]: Type, A: Type](top: Boolean, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]])(using Quotes) = { + def deriveCaseObject[F[_]: Type, A: Type](top: Boolean, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]])(using Quotes): Expr[F[A]] = { val summoned = summonOptionalIfNotTop[F, A](top) - '{$deriver.deriveRecord[A](Schema.force($schema).asInstanceOf[Schema.Record[A]], Chunk.empty, $summoned)} + Expr.summon[scala.reflect.ClassTag[A]] match { + case Some(classTag) => + '{ $deriver.tryDeriveRecord[A](Schema.force($schema), Chunk.empty, $summoned)($classTag) } + case None => + val typeRepr = TypeRepr.of[A] + report.errorAndAbort(s"Cannot find a ClassTag for ${typeRepr.show}") + } } - def deriveCaseClass[F[_]: Type, A: Type](top: Boolean, mirror: Mirror, stack: Stack, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]], selfRef: Ref)(using Quotes) = { + def deriveCaseClass[F[_]: Type, A: Type](top: Boolean, mirror: Mirror, stack: Stack, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]], selfRef: Ref)(using Quotes): Expr[F[A]] = { val newStack = stack.push(selfRef, TypeRepr.of[A]) val labels = mirror.labels.toList @@ -374,11 +380,22 @@ private case class DeriveInstance()(using val ctx: Quotes) extends ReflectionUti } val summoned = summonOptionalIfNotTop[F, A](top) - - lazyVals[F[A]](selfRef, - schemaRef -> '{Schema.force($schema).asInstanceOf[Schema.Record[A]]}, - selfRef -> '{$deriver.deriveRecord[A]($schemaRefExpr, Chunk(${Varargs(fieldInstances)}: _*), $summoned)} - ) + Expr.summon[scala.reflect.ClassTag[A]] match { + case Some(classTag) => + lazyVals[F[A]](selfRef, + schemaRef -> '{ + Schema.force($schema).asInstanceOf[Schema.Record[A]] + }, + selfRef -> '{ + $deriver.tryDeriveRecord[A](Schema.force($schema), Chunk(${ + Varargs(fieldInstances) + }: _*), $summoned)($classTag) + } + ) + case None => + val typeRepr = TypeRepr.of[A] + report.errorAndAbort(s"Cannot find a ClassTag for ${typeRepr.show}") + } } else { val (schemaRef, schemaRefExpr) = createSchemaRef[A, Schema.Transform[ListMap[String, _], A, _]](stack) val (recordSchemaRef, recordSchemaRefExpr) = createSchemaRef[ListMap[String, _], GenericRecord](stack) @@ -402,7 +419,7 @@ private case class DeriveInstance()(using val ctx: Quotes) extends ReflectionUti } } - def deriveEnum[F[_]: Type, A: Type](top: Boolean, mirror: Mirror, stack: Stack, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]], selfRef: Ref)(using Quotes) = { + def deriveEnum[F[_]: Type, A: Type](top: Boolean, mirror: Mirror, stack: Stack, deriver: Expr[Deriver[F]], schema: Expr[Schema[A]], selfRef: Ref)(using Quotes): Expr[F[A]] = { val newStack = stack.push(selfRef, TypeRepr.of[A]) val labels = mirror.labels.toList @@ -421,10 +438,22 @@ private case class DeriveInstance()(using val ctx: Quotes) extends ReflectionUti val summoned = summonOptionalIfNotTop[F, A](top) - lazyVals[F[A]](selfRef, - schemaRef -> '{Schema.force($schema).asInstanceOf[Schema.Enum[A]]}, - selfRef -> '{$deriver.deriveEnum[A]($schemaRefExpr, Chunk(${Varargs(caseInstances)}: _*), $summoned)} - ) + Expr.summon[scala.reflect.ClassTag[A]] match { + case Some(classTag) => + lazyVals[F[A]](selfRef, + schemaRef -> '{ + Schema.force($schema).asInstanceOf[Schema.Enum[A]] + }, + selfRef -> '{ + $deriver.tryDeriveEnum[A](Schema.force($schema), Chunk(${ + Varargs(caseInstances) + }: _*), $summoned)($classTag) + } + ) + case None => + val typeRepr = TypeRepr.of[A] + report.errorAndAbort(s"Cannot find a ClassTag for ${typeRepr.show}") + } } def lazyVals[A: Type](result: Ref, defs: (Ref, Expr[_])*)(using Quotes) = diff --git a/zio-schema-derivation/shared/src/main/scala/zio/schema/Deriver.scala b/zio-schema-derivation/shared/src/main/scala/zio/schema/Deriver.scala index ee2c698a6..0b0ffb5bb 100644 --- a/zio-schema-derivation/shared/src/main/scala/zio/schema/Deriver.scala +++ b/zio-schema-derivation/shared/src/main/scala/zio/schema/Deriver.scala @@ -37,6 +37,26 @@ trait Deriver[F[_]] extends VersionSpecificDeriver[F] { self => CachedDeriver.apply(this, cache) } + def tryDeriveRecord[A: ClassTag]( + schema: Schema[A], + fields: => Chunk[WrappedF[F, _]], + summoned: => Option[F[A]] + ): F[A] = + schema match { + case record: Schema.Record[A] => deriveRecord(record, fields, summoned) + case _ => deriveUnknown(summoned) + } + + def tryDeriveEnum[A: ClassTag]( + schema: Schema[A], + cases: => Chunk[WrappedF[F, _]], + summoned: => Option[F[A]] + ): F[A] = + schema match { + case e: Schema.Enum[A] => deriveEnum(e, cases, summoned) + case _ => deriveUnknown(summoned) + } + def deriveRecord[A](record: Schema.Record[A], fields: => Chunk[WrappedF[F, _]], summoned: => Option[F[A]]): F[A] def deriveEnum[A](`enum`: Schema.Enum[A], cases: => Chunk[WrappedF[F, _]], summoned: => Option[F[A]]): F[A]