Skip to content

Commit 38d7f2c

Browse files
authored
Merge branch 'main' into update/jackson-module-scala-2.21.0
2 parents 0570956 + f97dd97 commit 38d7f2c

File tree

8 files changed

+175
-23
lines changed

8 files changed

+175
-23
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
fail-fast: false
2222
steps:
2323
- name: Checkout current branch
24-
uses: actions/checkout@v6.0.1
24+
uses: actions/checkout@v6.0.2
2525
with:
2626
fetch-depth: 0
2727
- name: Setup Scala and Java
@@ -43,7 +43,7 @@ jobs:
4343
scala: ['2.12.21', '2.13.18', '3.3.7']
4444
platform: ['JVM', 'Native', 'JS']
4545
steps:
46-
- uses: actions/checkout@v6.0.1
46+
- uses: actions/checkout@v6.0.2
4747
with:
4848
fetch-depth: 0
4949
- uses: coursier/setup-action@v2
@@ -63,7 +63,7 @@ jobs:
6363
timeout-minutes: 20
6464
steps:
6565
- name: Checkout current branch
66-
uses: actions/checkout@v6.0.1
66+
uses: actions/checkout@v6.0.2
6767
with:
6868
fetch-depth: 300
6969
- name: Fetch tags
@@ -86,7 +86,7 @@ jobs:
8686
needs: [build,lint]
8787
if: ${{ github.event_name != 'pull_request' }}
8888
steps:
89-
- uses: actions/checkout@v6.0.1
89+
- uses: actions/checkout@v6.0.2
9090
with:
9191
fetch-depth: 0
9292
- uses: coursier/setup-action@v2

.github/workflows/site.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
if: ${{ github.event_name == 'pull_request' }}
1919
steps:
2020
- name: Git Checkout
21-
uses: actions/checkout@v6.0.1
21+
uses: actions/checkout@v6.0.2
2222
with:
2323
fetch-depth: '0'
2424
- uses: coursier/setup-action@v2
@@ -34,7 +34,7 @@ jobs:
3434
if: ${{ ((github.event_name == 'release') && (github.event.action == 'published')) || (github.event_name == 'workflow_dispatch') }}
3535
steps:
3636
- name: Git Checkout
37-
uses: actions/checkout@v6.0.1
37+
uses: actions/checkout@v6.0.2
3838
with:
3939
fetch-depth: '0'
4040
- uses: coursier/setup-action@v2
@@ -55,7 +55,7 @@ jobs:
5555
if: ${{ (github.event_name == 'push') || ((github.event_name == 'release') && (github.event.action == 'published')) }}
5656
steps:
5757
- name: Git Checkout
58-
uses: actions/checkout@v6.0.1
58+
uses: actions/checkout@v6.0.2
5959
with:
6060
ref: ${{ github.head_ref }}
6161
fetch-depth: '0'
@@ -71,7 +71,7 @@ jobs:
7171
git add README.md
7272
git commit -m "Update README.md" || echo "No changes to commit"
7373
- name: Create Pull Request
74-
uses: peter-evans/create-pull-request@v8.0.0
74+
uses: peter-evans/create-pull-request@v8.1.0
7575
with:
7676
body: |-
7777
Autogenerated changes after running the `sbt docs/generateReadme` command of the [zio-sbt-website](https://zio.dev/zio-sbt) plugin.

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.12.0
1+
sbt.version=1.12.1

project/plugins.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")
77
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1")
88
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.2")
99
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
10-
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9")
10+
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.10")
1111
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8")
1212
addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.4.10")
1313
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4")

zio-schema-derivation/shared/src/main/scala-2/zio/schema/DeriveSchema.scala

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,21 @@ object DeriveSchema {
242242
if (tpe.typeArgs.isEmpty) Nil
243243
else {
244244
val typeMembers = tpe.typeSymbol.asClass.typeParams.map(decodeName)
245-
val typeArgs = tpe.typeArgs
246-
.map(_.typeSymbol.fullName)
247-
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
245+
val typeArgs = tpe.typeArgs.map { typeArg =>
246+
val sym = typeArg.typeSymbol
247+
if (sym.isAbstract || sym.isParameter || sym.fullName
248+
.startsWith("<") || sym.fullName == "scala.Nothing") {
249+
val schemaType = c.typecheck(tq"_root_.zio.schema.Schema[$typeArg]", c.TYPEmode).tpe
250+
c.inferImplicitValue(schemaType, withMacrosDisabled = true) match {
251+
case EmptyTree =>
252+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
253+
case schemaTree =>
254+
q"_root_.zio.schema.Schema.getTypeId($schemaTree).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
255+
}
256+
} else {
257+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
258+
}
259+
}
248260
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
249261
List(
250262
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"
@@ -588,9 +600,20 @@ object DeriveSchema {
588600
if (tpe.typeArgs.isEmpty) Nil
589601
else {
590602
val typeMembers = tpe.typeSymbol.asClass.typeParams.map(decodeName)
591-
val typeArgs = tpe.typeArgs
592-
.map(_.typeSymbol.fullName)
593-
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
603+
val typeArgs = tpe.typeArgs.map { typeArg =>
604+
val sym = typeArg.typeSymbol
605+
if (sym.isAbstract || sym.isParameter || sym.fullName.startsWith("<") || sym.fullName == "scala.Nothing") {
606+
val schemaType = c.typecheck(tq"_root_.zio.schema.Schema[$typeArg]", c.TYPEmode).tpe
607+
c.inferImplicitValue(schemaType, withMacrosDisabled = true) match {
608+
case EmptyTree =>
609+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
610+
case schemaTree =>
611+
q"_root_.zio.schema.Schema.getTypeId($schemaTree).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
612+
}
613+
} else {
614+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
615+
}
616+
}
594617
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
595618
List(
596619
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"
@@ -646,9 +669,20 @@ object DeriveSchema {
646669
if (subtype.typeArgs.isEmpty) Nil
647670
else {
648671
val typeMembers = subtype.typeSymbol.asClass.typeParams.map(decodeName)
649-
val typeArgs = subtype.typeArgs
650-
.map(_.typeSymbol.fullName)
651-
.map(t => q"_root_.zio.schema.TypeId.parse(${t}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]")
672+
val typeArgs = subtype.typeArgs.map { typeArg =>
673+
val sym = typeArg.typeSymbol
674+
if (sym.isAbstract || sym.isParameter || sym.fullName.startsWith("<") || sym.fullName == "scala.Nothing") {
675+
val schemaType = c.typecheck(tq"_root_.zio.schema.Schema[$typeArg]", c.TYPEmode).tpe
676+
c.inferImplicitValue(schemaType, withMacrosDisabled = true) match {
677+
case EmptyTree =>
678+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
679+
case schemaTree =>
680+
q"_root_.zio.schema.Schema.getTypeId($schemaTree).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
681+
}
682+
} else {
683+
q"_root_.zio.schema.TypeId.parse(${sym.fullName}).asInstanceOf[_root_.zio.schema.TypeId.Nominal]"
684+
}
685+
}
652686
val typeMembersWithArgs = typeMembers.zip(typeArgs).map { case (m, a) => q"($m, $a)" }
653687
List(
654688
q"new _root_.zio.schema.annotation.genericTypeInfo(_root_.scala.collection.immutable.ListMap(..$typeMembersWithArgs))"

zio-schema-derivation/shared/src/main/scala-3/zio/schema/DeriveSchema.scala

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,24 @@ private case class DeriveSchema()(using val ctx: Quotes) {
197197
val annotationExprs = filteredAnnotationExprs(if (isEnumTerm) repr.termSymbol else repr.typeSymbol)
198198
val genericAnnotations = if (repr.classSymbol.exists(_.typeMembers.nonEmpty)){
199199
val typeMembersExpr = Expr.ofSeq(repr.classSymbol.get.typeMembers.map { t => Expr(t.name) })
200-
val typeArgsExpr = Expr.ofSeq(repr.typeArgs.map { t => Expr(t.typeSymbol.fullName) })
201-
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip(${typeArgsExpr}.map(name => TypeId.parse(name).asInstanceOf[TypeId.Nominal]))))})
200+
val typeArgsExprs: List[Expr[TypeId.Nominal]] = repr.typeArgs.map { t =>
201+
val sym = t.typeSymbol
202+
if (sym.isTypeParam || sym.fullName.startsWith("<") || sym.fullName == "scala.Nothing") {
203+
t.asType match {
204+
case '[tt] =>
205+
Expr.summon[Schema[tt]] match {
206+
case Some(schemaExpr) =>
207+
'{ Schema.getTypeId($schemaExpr).asInstanceOf[TypeId.Nominal] }
208+
case None =>
209+
'{ TypeId.parse(${Expr(sym.fullName)}).asInstanceOf[TypeId.Nominal] }
210+
}
211+
}
212+
} else {
213+
'{ TypeId.parse(${Expr(sym.fullName)}).asInstanceOf[TypeId.Nominal] }
214+
}
215+
}
216+
val typeArgsExpr = Expr.ofSeq(typeArgsExprs)
217+
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip($typeArgsExpr)))})
202218
} else List.empty
203219
val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(genericAnnotations)}) }
204220
val typeInfo = '{TypeId.parse(${Expr(repr.classSymbol.get.fullName.replaceAll("\\$", ""))})}
@@ -380,8 +396,24 @@ private case class DeriveSchema()(using val ctx: Quotes) {
380396
}
381397
val genericAnnotations = if (repr.classSymbol.exists(_.typeMembers.nonEmpty)){
382398
val typeMembersExpr = Expr.ofSeq(repr.classSymbol.get.typeMembers.map { t => Expr(t.name) })
383-
val typeArgsExpr = Expr.ofSeq(repr.typeArgs.map { t => Expr(t.typeSymbol.fullName) })
384-
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip(${typeArgsExpr}.map(name => TypeId.parse(name).asInstanceOf[TypeId.Nominal]))))})
399+
val typeArgsExprs: List[Expr[TypeId.Nominal]] = repr.typeArgs.map { t =>
400+
val sym = t.typeSymbol
401+
if (sym.isTypeParam || sym.fullName.startsWith("<") || sym.fullName == "scala.Nothing") {
402+
t.asType match {
403+
case '[tt] =>
404+
Expr.summon[Schema[tt]] match {
405+
case Some(schemaExpr) =>
406+
'{ Schema.getTypeId($schemaExpr).asInstanceOf[TypeId.Nominal] }
407+
case None =>
408+
'{ TypeId.parse(${Expr(sym.fullName)}).asInstanceOf[TypeId.Nominal] }
409+
}
410+
}
411+
} else {
412+
'{ TypeId.parse(${Expr(sym.fullName)}).asInstanceOf[TypeId.Nominal] }
413+
}
414+
}
415+
val typeArgsExpr = Expr.ofSeq(typeArgsExprs)
416+
List('{zio.schema.annotation.genericTypeInfo(ListMap.from(${typeMembersExpr}.zip($typeArgsExpr)))})
385417
} else List.empty
386418
val annotations = '{ zio.Chunk.fromIterable(${Expr.ofSeq(annotationExprs)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(docAnnotationExpr.toList)}) ++ zio.Chunk.fromIterable(${Expr.ofSeq(genericAnnotations)}) }
387419

zio-schema-derivation/shared/src/test/scala/zio/schema/DeriveSpec.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ import zio.{ Chunk, Scope }
210210
.exists(a => a.isInstanceOf[fieldDefaultValue[_]] && a.asInstanceOf[fieldDefaultValue[Int]].value == 52),
211211
capturedSchema.schema.defaultValue == Right(RecordWithDefaultValue(42, 52))
212212
)
213+
},
214+
test("genericTypeInfo resolves abstract type parameters via Schema") {
215+
val capturedSchema = Derive.derive[CapturedSchema, GenericType[Int]](schemaCapturer)
216+
val maybeTypeInfo = capturedSchema.schema.annotations.collectFirst { case gt @ genericTypeInfo(_) => gt }
217+
assertTrue(
218+
maybeTypeInfo.contains(
219+
genericTypeInfo(ListMap("T" -> TypeId.parse("scala.Int").asInstanceOf[TypeId.Nominal]))
220+
)
221+
)
213222
}
214223
),
215224
suite("support factory")(
@@ -351,6 +360,13 @@ import zio.{ Chunk, Scope }
351360
DeriveSchema.gen[GenericRecordWithDefaultValue[Int]]
352361
}
353362

363+
case class GenericType[T](value: T)
364+
365+
object GenericType {
366+
implicit def schema[T](implicit schemaT: Schema[T]): Schema[GenericType[T]] =
367+
DeriveSchema.gen[GenericType[T]]
368+
}
369+
354370
sealed trait Enum1
355371

356372
object Enum1 {

zio-schema/shared/src/main/scala/zio/schema/Schema.scala

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,76 @@ object Schema extends SchemaPlatformSpecific with SchemaEquality with SchemaVers
189189
case _ => schema
190190
}
191191

192+
def getTypeId[A](schema: Schema[A]): TypeId =
193+
schema match {
194+
case record: Record[_] => record.id
195+
case enum0: Enum[_] => enum0.id
196+
case Primitive(standardType, _) =>
197+
val typeName = standardType.tag match {
198+
case "unit" => "scala.Unit"
199+
case "string" => "java.lang.String"
200+
case "boolean" => "scala.Boolean"
201+
case "byte" => "scala.Byte"
202+
case "short" => "scala.Short"
203+
case "int" => "scala.Int"
204+
case "long" => "scala.Long"
205+
case "float" => "scala.Float"
206+
case "double" => "scala.Double"
207+
case "binary" => "zio.Chunk"
208+
case "char" => "scala.Char"
209+
case "bigDecimal" => "java.math.BigDecimal"
210+
case "bigInteger" => "java.math.BigInteger"
211+
case "dayOfWeek" => "java.time.DayOfWeek"
212+
case "month" => "java.time.Month"
213+
case "monthDay" => "java.time.MonthDay"
214+
case "period" => "java.time.Period"
215+
case "year" => "java.time.Year"
216+
case "yearMonth" => "java.time.YearMonth"
217+
case "zoneId" => "java.time.ZoneId"
218+
case "zoneOffset" => "java.time.ZoneOffset"
219+
case "duration" => "java.time.Duration"
220+
case "instant" => "java.time.Instant"
221+
case "localDate" => "java.time.LocalDate"
222+
case "localTime" => "java.time.LocalTime"
223+
case "localDateTime" => "java.time.LocalDateTime"
224+
case "offsetTime" => "java.time.OffsetTime"
225+
case "offsetDateTime" => "java.time.OffsetDateTime"
226+
case "zonedDateTime" => "java.time.ZonedDateTime"
227+
case "uuid" => "java.util.UUID"
228+
case "currency" => "java.util.Currency"
229+
case other => other
230+
}
231+
TypeId.parse(typeName)
232+
case Transform(schema, _, _, _, _) => getTypeId(schema)
233+
case Lazy(schema0) => getTypeId(schema0())
234+
case Optional(schema, _) => TypeId.parse("scala.Option")
235+
case Sequence(_, _, _, _, identity) =>
236+
identity match {
237+
case s: String if s == "Chunk" => TypeId.parse("zio.Chunk")
238+
case s: String if s == "List" => TypeId.parse("scala.collection.immutable.List")
239+
case s: String if s == "Vector" => TypeId.parse("scala.collection.immutable.Vector")
240+
case s: String if s == "Set" => TypeId.parse("scala.collection.immutable.Set")
241+
case s: String if s == "NonEmptyChunk" => TypeId.parse("zio.NonEmptyChunk")
242+
case s: String => TypeId.parse(s)
243+
case _ => TypeId.parse("zio.Chunk")
244+
}
245+
case NonEmptySequence(_, _, _, _, identity) =>
246+
identity match {
247+
case s: String if s == "NonEmptyChunk" => TypeId.parse("zio.NonEmptyChunk")
248+
case s: String if s == "NonEmptySet" => TypeId.parse("zio.prelude.NonEmptySet")
249+
case s: String => TypeId.parse(s)
250+
case _ => TypeId.parse("zio.NonEmptyChunk")
251+
}
252+
case _: Set[_] => TypeId.parse("scala.collection.immutable.Set")
253+
case _: Map[_, _] => TypeId.parse("scala.collection.immutable.Map")
254+
case _: NonEmptyMap[_, _] => TypeId.parse("zio.prelude.NonEmptyMap")
255+
case _: Tuple2[_, _] => TypeId.parse("scala.Tuple2")
256+
case _: Either[_, _] => TypeId.parse("scala.Either")
257+
case _: Fallback[_, _] => TypeId.parse("zio.schema.Fallback")
258+
case _: Dynamic => TypeId.parse("zio.schema.DynamicValue")
259+
case Fail(_, _) => TypeId.Structural
260+
}
261+
192262
def record(id: TypeId, fields: Field[ListMap[String, _], _]*): Schema[ListMap[String, _]] =
193263
GenericRecord(id, FieldSet(fields: _*))
194264

0 commit comments

Comments
 (0)