Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save info about generically applied types in new annotation (#695) #703

Merged
merged 1 commit into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,20 @@ object DeriveSchema {

val typeId = q"_root_.zio.schema.TypeId.parse(${tpe.typeSymbol.fullName})"

val typeAnnotations: List[Tree] = collectTypeAnnotations(tpe)
val genericAnnotations: List[Tree] =
if (tpe.typeArgs.isEmpty) Nil
else {
val typeMembers = tpe.typeSymbol.asClass.typeParams.map(_.name.toString)
val typeArgs = tpe.typeArgs
.map(_.typeSymbol.fullName)
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
List(
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"
)
}

val typeAnnotations: List[Tree] = collectTypeAnnotations(tpe) ++ genericAnnotations

val defaultConstructorValues =
tpe.typeSymbol.asClass.primaryConstructor.asMethod.paramLists.head
Expand Down Expand Up @@ -557,7 +570,20 @@ object DeriveSchema {
val hasSimpleEnum: Boolean =
tpe.typeSymbol.annotations.exists(_.tree.tpe =:= typeOf[_root_.zio.schema.annotation.simpleEnum])

val typeAnnotations: List[Tree] = (isSimpleEnum, hasSimpleEnum) match {
val genericAnnotations: List[Tree] =
if (tpe.typeArgs.isEmpty) Nil
else {
val typeMembers = tpe.typeSymbol.asClass.typeParams.map(_.name.toString)
val typeArgs = tpe.typeArgs
.map(_.typeSymbol.fullName)
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
List(
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"
)
}

val typeAnnotations: List[Tree] = ((isSimpleEnum, hasSimpleEnum) match {
case (true, false) =>
tpe.typeSymbol.annotations.collect {
case annotation if !(annotation.tree.tpe <:< JavaAnnotationTpe) =>
Expand Down Expand Up @@ -592,7 +618,7 @@ object DeriveSchema {
c.warning(c.enclosingPosition, s"Unhandled annotation ${annotation.tree}")
EmptyTree
}.filter(_ != EmptyTree)
}
}) ++ genericAnnotations

val selfRefName = c.freshName("ref")
val selfRefIdent = Ident(TermName(selfRefName))
Expand All @@ -602,6 +628,19 @@ object DeriveSchema {
val typeArgs = subtypes ++ Iterable(tpe)

val cases = subtypes.map { (subtype: Type) =>
val genericAnnotations: List[Tree] =
if (subtype.typeArgs.isEmpty) Nil
else {
val typeMembers = subtype.typeSymbol.asClass.typeParams.map(_.name.toString)
val typeArgs = subtype.typeArgs
.map(_.typeSymbol.fullName)
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
List(
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"
)
}

val typeAnnotations: List[Tree] =
subtype.typeSymbol.annotations.collect {
case annotation if !(annotation.tree.tpe <:< JavaAnnotationTpe) =>
Expand All @@ -617,7 +656,7 @@ object DeriveSchema {
case annotation =>
c.warning(c.enclosingPosition, s"Unhandled annotation ${annotation.tree}")
EmptyTree
}.filter(_ != EmptyTree)
}.filter(_ != EmptyTree) ++ genericAnnotations

val caseLabel = subtype.typeSymbol.name.toString.trim
val caseSchema = directInferSchema(tpe, concreteType(tpe, subtype), currentFrame +: stack)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,12 @@ private case class DeriveSchema()(using val ctx: Quotes) {
then TypeRepr.of[T].typeSymbol.children.map(_.annotations).flatten.filter (filterAnnotation).map (_.asExpr)
else TypeRepr.of[T].typeSymbol.annotations.filter (filterAnnotation).map (_.asExpr)

val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr)}) }
val genericAnnotations = if (TypeRepr.of[T].classSymbol.exists(_.typeMembers.nonEmpty)){
val typeMembersExpr = Expr.ofSeq(TypeRepr.of[T].classSymbol.get.typeMembers.map { t => Expr(t.name) })
val typeArgsExpr = Expr.ofSeq(TypeRepr.of[T].typeArgs.map { t => Expr(t.typeSymbol.fullName) })
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip(${typeArgsExpr}.map(name => TypeId.parse(name).asInstanceOf[TypeId.Nominal]))))})
} else List.empty
val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(genericAnnotations)}) }
val typeInfo = '{TypeId.parse(${Expr(TypeRepr.of[T].show)})}

val applied = if (labels.length <= 22) {
Expand Down Expand Up @@ -372,7 +377,12 @@ private case class DeriveSchema()(using val ctx: Quotes) {
case (false, true) => throw new Exception(s"${TypeRepr.of[T].typeSymbol.name} must be a simple Enum")
case _ => TypeRepr.of[T].typeSymbol.annotations.filter(filterAnnotation).map(_.asExpr)
}
val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr.toList)}) }
val genericAnnotations = if (TypeRepr.of[T].classSymbol.exists(_.typeMembers.nonEmpty)){
val typeMembersExpr = Expr.ofSeq(TypeRepr.of[T].classSymbol.get.typeMembers.map { t => Expr(t.name) })
val typeArgsExpr = Expr.ofSeq(TypeRepr.of[T].typeArgs.map { t => Expr(t.typeSymbol.fullName) })
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip(${typeArgsExpr}.map(name => TypeId.parse(name).asInstanceOf[TypeId.Nominal]))))})
} else List.empty
val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr.toList)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(genericAnnotations)}) }

val typeInfo = '{TypeId.parse(${Expr(TypeRepr.of[T].show)})}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package zio.schema

import scala.annotation.nowarn
import scala.collection.immutable.ListMap
import scala.reflect.ClassTag

import zio.schema.Deriver.WrappedF
import zio.schema.Schema.Field
import zio.schema.annotation.fieldDefaultValue
import zio.schema.annotation.{ fieldDefaultValue, genericTypeInfo }
import zio.test.{ Spec, TestEnvironment, ZIOSpecDefault, assertTrue }
import zio.{ Chunk, Scope }

Expand Down Expand Up @@ -183,7 +184,11 @@ import zio.{ Chunk, Scope }
.asInstanceOf[Schema.Record[GenericRecordWithDefaultValue[Int]]]
.fields(0)
.annotations
val maybeTypeInfo = capturedSchema.schema.annotations.collectFirst { case gt @ genericTypeInfo(_) => gt }
assertTrue(
maybeTypeInfo.contains(
genericTypeInfo(ListMap("T" -> TypeId.parse("scala.Int").asInstanceOf[TypeId.Nominal]))
),
annotations.exists { a =>
a.isInstanceOf[fieldDefaultValue[_]] &&
a.asInstanceOf[fieldDefaultValue[Option[Int]]].value == None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object DefaultValueSpec extends ZIOSpecDefault {

def spec: Spec[TestEnvironment, Any] =
suite("Custom Spec")(
customSuite,
customSuite
) @@ timeout(90.seconds)

private val customSuite = suite("custom")(
Expand All @@ -24,7 +24,7 @@ object DefaultValueSpec extends ZIOSpecDefault {
)
)

case class WithDefaultValue(orderId:Int, description: String = "desc")
case class WithDefaultValue(orderId: Int, description: String = "desc")

object WithDefaultValue {
implicit lazy val schema: Schema[WithDefaultValue] = DeriveSchema.gen[WithDefaultValue]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package zio.schema.annotation

import scala.collection.immutable.ListMap

import zio.schema.TypeId

final case class genericTypeInfo(appliedTypes: ListMap[String, TypeId.Nominal])
extends scala.annotation.StaticAnnotation
Loading