Skip to content

Commit

Permalink
Extensible MetaSchema (zio#454)
Browse files Browse the repository at this point in the history
* Extensible MetaSchema

* Scala3 fix

* Scalafix

* Type alias to case class
  • Loading branch information
vigoo authored Dec 7, 2022
1 parent 04cc81b commit 1190d93
Show file tree
Hide file tree
Showing 11 changed files with 1,233 additions and 837 deletions.
201 changes: 148 additions & 53 deletions tests/shared/src/test/scala-2/zio/schema/MetaSchemaSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package zio.schema
import scala.collection.immutable.ListMap

import zio._
import zio.constraintless.TypeList._
import zio.schema.CaseSet._
import zio.schema.SchemaAssertions._
import zio.schema.meta.{ MetaSchema, NodePath }
import zio.schema.meta.ExtensibleMetaSchema.Labelled
import zio.schema.meta.{ ExtensibleMetaSchema, MetaSchema, NodePath }
import zio.test._

object MetaSchemaSpec extends ZIOSpecDefault {
Expand All @@ -15,15 +17,17 @@ object MetaSchemaSpec extends ZIOSpecDefault {
test("primitive") {
check(SchemaGen.anyPrimitive) {
case s @ Schema.Primitive(typ, _) =>
assertTrue(MetaSchema.fromSchema(s) == MetaSchema.Value(typ))
assertTrue(MetaSchema.fromSchema(s) == ExtensibleMetaSchema.Value[DynamicValue :: End](typ))
}
}
),
suite("optional")(
test("primitive") {
check(SchemaGen.anyPrimitive) {
case s @ Schema.Primitive(typ, _) =>
assertTrue(MetaSchema.fromSchema(s.optional) == MetaSchema.Value(typ, optional = true))
assertTrue(
MetaSchema.fromSchema(s.optional) == ExtensibleMetaSchema.Value[DynamicValue :: End](typ, optional = true)
)
}
}
),
Expand All @@ -32,8 +36,11 @@ object MetaSchemaSpec extends ZIOSpecDefault {
check(SchemaGen.anyPrimitive) {
case s @ Schema.Primitive(typ, _) =>
assertTrue(
MetaSchema.fromSchema(Schema.chunk(s)) == MetaSchema
.ListNode(MetaSchema.Value(typ, path = NodePath.root / "item"), NodePath.root)
MetaSchema.fromSchema(Schema.chunk(s)) == ExtensibleMetaSchema
.ListNode(
ExtensibleMetaSchema.Value[DynamicValue :: End](typ, path = NodePath.root / "item"),
NodePath.root
)
)
}
}
Expand Down Expand Up @@ -61,29 +68,43 @@ object MetaSchemaSpec extends ZIOSpecDefault {
)
)
val expectedAst =
MetaSchema.Product(
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.MetaSchema.Product"),
path = NodePath.root,
fields = Chunk(
("a", MetaSchema.Value(StandardType.StringType, NodePath.root / "a")),
("b", MetaSchema.Value(StandardType.IntType, NodePath.root / "b"))
Labelled(
"a",
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.StringType, NodePath.root / "a")
),
Labelled("b", ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root / "b"))
)
)
assertTrue(MetaSchema.fromSchema(schema) == expectedAst)
},
test("case class") {
val schema = Schema[SchemaGen.Arity2]
val expectedAst =
MetaSchema.Product(
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity2"),
path = NodePath.root,
fields = Chunk(
"value1" -> MetaSchema.Value(StandardType.StringType, NodePath.root / "value1"),
"value2" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "value2",
fields = Chunk(
"value" -> MetaSchema.Value(StandardType.IntType, NodePath.root / "value2" / "value")
Labelled(
"value1",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.StringType, NodePath.root / "value1")
),
Labelled(
"value2",
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "value2",
fields = Chunk(
Labelled(
"value",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.IntType, NodePath.root / "value2" / "value")
)
)
)
)
)
Expand All @@ -96,17 +117,17 @@ object MetaSchemaSpec extends ZIOSpecDefault {
val ast = MetaSchema.fromSchema(schema)

val recursiveRef: Option[MetaSchema] = ast match {
case MetaSchema.Product(_, _, elements, _) =>
case ExtensibleMetaSchema.Product(_, _, elements, _) =>
elements.find {
case ("r", _) => true
case _ => false
}.map(_._2)
case Labelled("r", _) => true
case _ => false
}.map(_.schema)
case _ => None
}
assertTrue(
recursiveRef.exists {
case MetaSchema.Ref(pathRef, _, _) => pathRef == Chunk.empty
case _ => false
case ExtensibleMetaSchema.Ref(pathRef, _, _) => pathRef == Chunk.empty
case _ => false
}
)
}
Expand All @@ -123,28 +144,51 @@ object MetaSchemaSpec extends ZIOSpecDefault {
)(_.asInstanceOf[SchemaGen.Arity2])(_.asInstanceOf[Any])(_.isInstanceOf[Any])
)
val expectedAst =
MetaSchema.Sum(
ExtensibleMetaSchema.Sum(
TypeId.Structural,
path = NodePath.root,
cases = Chunk(
"type1" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "type1",
fields = Chunk(
"value" -> MetaSchema.Value(StandardType.IntType, NodePath.root / "type1" / "value")
Labelled(
"type1",
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "type1",
fields = Chunk(
Labelled(
"value",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.IntType, NodePath.root / "type1" / "value")
)
)
)
),
"type2" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity2"),
path = NodePath.root / "type2",
fields = Chunk(
"value1" -> MetaSchema.Value(StandardType.StringType, NodePath.root / "type2" / "value1"),
"value2" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "type2" / "value2",
fields = Chunk(
"value" -> MetaSchema
.Value(StandardType.IntType, NodePath.root / "type2" / "value2" / "value")
Labelled(
"type2",
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity2"),
path = NodePath.root / "type2",
fields = Chunk(
Labelled(
"value1",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.StringType, NodePath.root / "type2" / "value1")
),
Labelled(
"value2",
ExtensibleMetaSchema.Product(
id = TypeId.parse("zio.schema.SchemaGen.Arity1"),
path = NodePath.root / "type2" / "value2",
fields = Chunk(
Labelled(
"value",
ExtensibleMetaSchema
.Value[DynamicValue :: End](
StandardType.IntType,
NodePath.root / "type2" / "value2" / "value"
)
)
)
)
)
)
)
Expand All @@ -155,26 +199,49 @@ object MetaSchemaSpec extends ZIOSpecDefault {
},
test("sealed trait") {
val schema = Schema[Pet]
val expectedAst = MetaSchema.Sum(
val expectedAst = ExtensibleMetaSchema.Sum[DynamicValue :: End](
TypeId.parse("zio.schema.MetaSchemaSpec.Pet"),
path = NodePath.root,
cases = Chunk(
"Rock" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.MetaSchemaSpec.Rock"),
path = NodePath.root / "Rock",
fields = Chunk.empty
Labelled(
"Rock",
ExtensibleMetaSchema.Product[DynamicValue :: End](
id = TypeId.parse("zio.schema.MetaSchemaSpec.Rock"),
path = NodePath.root / "Rock",
fields = Chunk.empty
)
),
"Dog" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.MetaSchemaSpec.Dog"),
path = NodePath.root / "Dog",
fields = Chunk("name" -> MetaSchema.Value(StandardType.StringType, NodePath.root / "Dog" / "name"))
Labelled(
"Dog",
ExtensibleMetaSchema.Product[DynamicValue :: End](
id = TypeId.parse("zio.schema.MetaSchemaSpec.Dog"),
path = NodePath.root / "Dog",
fields = Chunk(
Labelled(
"name",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.StringType, NodePath.root / "Dog" / "name")
)
)
)
),
"Cat" -> MetaSchema.Product(
id = TypeId.parse("zio.schema.MetaSchemaSpec.Cat"),
path = NodePath.root / "Cat",
fields = Chunk(
"name" -> MetaSchema.Value(StandardType.StringType, NodePath.root / "Cat" / "name"),
"hasHair" -> MetaSchema.Value(StandardType.BoolType, NodePath.root / "Cat" / "hasHair")
Labelled(
"Cat",
ExtensibleMetaSchema.Product[DynamicValue :: End](
id = TypeId.parse("zio.schema.MetaSchemaSpec.Cat"),
path = NodePath.root / "Cat",
fields = Chunk(
Labelled(
"name",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.StringType, NodePath.root / "Cat" / "name")
),
Labelled(
"hasHair",
ExtensibleMetaSchema
.Value[DynamicValue :: End](StandardType.BoolType, NodePath.root / "Cat" / "hasHair")
)
)
)
)
)
Expand Down Expand Up @@ -253,6 +320,27 @@ object MetaSchemaSpec extends ZIOSpecDefault {
assert(MetaSchema.fromSchema(schema).toSchema)(hasSameSchemaStructure(schema))
}
}
),
suite("extended meta schema")(
test("represents known type as Known") {
val meta1 = ExtendedMetaSchema.fromSchema(Schema[Pet])
val meta2 = ExtendedMetaSchema.fromSchema(Schema[DynamicValue])
val meta3 = ExtendedMetaSchema.fromSchema(Schema[Recursive])

assertTrue(
meta1.isInstanceOf[ExtensibleMetaSchema.Known[_]],
meta2.isInstanceOf[ExtensibleMetaSchema.Known[_]],
meta3.isInstanceOf[ExtensibleMetaSchema.Product[_]]
)
},
test("materializes the original schema") {
val meta1 = ExtendedMetaSchema.fromSchema(Schema[Pet])
val meta2 = ExtendedMetaSchema.fromSchema(Schema[DynamicValue])

val refEq1 = meta1.toSchema eq Schema[Pet]
val refEq2 = meta2.toSchema eq Schema[DynamicValue]
assertTrue(refEq1, refEq2)
}
)
)

Expand Down Expand Up @@ -288,4 +376,11 @@ object MetaSchemaSpec extends ZIOSpecDefault {
implicit lazy val schema: Schema[Pet] = DeriveSchema.gen[Pet]
}

type ExtendedMetaSchema = ExtensibleMetaSchema[DynamicValue :: Pet :: End]

object ExtendedMetaSchema {

def fromSchema[A](schema: Schema[A]): ExtendedMetaSchema =
ExtensibleMetaSchema.fromSchema(schema)
}
}
42 changes: 30 additions & 12 deletions tests/shared/src/test/scala/zio/schema/MigrationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package zio.schema
import scala.collection.immutable.ListMap

import zio._
import zio.schema.meta.{ MetaSchema, Migration, NodePath }
import zio.constraintless.TypeList._
import zio.schema.meta.{ ExtensibleMetaSchema, MetaSchema, Migration, NodePath }
import zio.schema.syntax._
import zio.test._

Expand All @@ -13,36 +14,44 @@ object MigrationSpec extends ZIOSpecDefault {
suite("Derivation")(
suite("Value")(
test("change type") {
val from = MetaSchema.Value(StandardType.IntType, NodePath.root)
val to = MetaSchema.Value(StandardType.StringType, NodePath.root)
val from = ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root)
val to = ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.StringType, NodePath.root)

assertTrue(
Migration
.derive(from, to) == Right(Chunk(Migration.ChangeType(NodePath.root, StandardType.StringType)))
)
},
test("optional") {
val from = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = false)
val to = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true)
val from =
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = false)
val to = ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true)

assertTrue(
Migration
.derive(from, to) == Right(Chunk(Migration.Optional(NodePath.root)))
)
},
test("require") {
val from = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true)
val to = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = false)
val from =
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true)
val to =
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = false)

assertTrue(
Migration
.derive(from, to) == Right(Chunk(Migration.Require(NodePath.root)))
)
},
test("increment dimensions") {
val from = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true)
val from =
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true)
val to =
MetaSchema.ListNode(MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true), NodePath.root)
ExtensibleMetaSchema
.ListNode(
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true),
NodePath.root
)

assertTrue(
Migration
Expand All @@ -51,8 +60,12 @@ object MigrationSpec extends ZIOSpecDefault {
},
test("decrement dimensions") {
val from =
MetaSchema.ListNode(MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true), NodePath.root)
val to = MetaSchema.Value(StandardType.IntType, NodePath.root, optional = true)
ExtensibleMetaSchema
.ListNode(
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true),
NodePath.root
)
val to = ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.IntType, NodePath.root, optional = true)

assertTrue(
Migration
Expand Down Expand Up @@ -205,7 +218,12 @@ object MigrationSpec extends ZIOSpecDefault {
test("ignore add case") {
val value: Pet1 = Pet1.Dog("name")
val dynamicValue = value.dynamic
assert(Migration.AddCase(NodePath.root, MetaSchema.Value(StandardType.UnitType, NodePath.root)))(
assert(
Migration.AddCase(
NodePath.root,
ExtensibleMetaSchema.Value[DynamicValue :: End](StandardType.UnitType, NodePath.root)
)
)(
transformsValueTo(value, dynamicValue)
)
},
Expand Down
Loading

0 comments on commit 1190d93

Please sign in to comment.