Skip to content

Commit

Permalink
Fallback to deriveUnknown if the schema is not the expected schema (#514
Browse files Browse the repository at this point in the history
)

* Fallback to deriveUnknown if the schema is not the expected record/enum schema

* Regenerated readme
  • Loading branch information
vigoo authored Feb 17, 2023
1 parent 6835a62 commit db9c908
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 20 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}"""
}
Expand All @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit db9c908

Please sign in to comment.