zio.schema.SchemaOrdering.scala Maven / Gradle / Ivy
The newest version!
package zio.schema
import scala.annotation.tailrec
import zio.Chunk
import zio.schema.DynamicValue._
import zio.schema.StandardType.UnitType
object SchemaOrdering {
private[schema] def ordering[A](schema: Schema[A]): Ordering[A] = (l: A, r: A) => {
compareBySchema(schema)(schema.toDynamic(l), schema.toDynamic(r))
}
private def compareBySchema[A](schema: Schema[A])(l: DynamicValue, r: DynamicValue): Int = (schema, l, r) match {
case (schema: Schema.Lazy[_], l, r) => compareBySchema(schema.schema)(l, r)
case (schema: Schema.Primitive[t], Primitive(lVal, lType), Primitive(rVal, rType))
if lType == rType && schema.standardType == lType =>
val lTypeCoerced = lType.asInstanceOf[StandardType[t]]
lTypeCoerced.compare(lVal.asInstanceOf[t], rVal.asInstanceOf[t])
case (Schema.Primitive(UnitType, _), _, _) => 0
case (Schema.Either(leftSchema, _, _), LeftValue(lVal), LeftValue(rVal)) =>
compareBySchema(leftSchema)(lVal, rVal)
case (Schema.Either(_, rightSchema, _), RightValue(lVal), RightValue(rVal)) =>
compareBySchema(rightSchema)(lVal, rVal)
case (Schema.Either(_, _, _), LeftValue(_), RightValue(_)) => -1
case (Schema.Either(_, _, _), RightValue(_), LeftValue(_)) => 1
case (Schema.Optional(innerSchema, _), SomeValue(lVal), SomeValue(rVal)) =>
compareBySchema(innerSchema)(lVal, rVal)
case (Schema.Optional(_, _), NoneValue, SomeValue(_)) => -1
case (Schema.Optional(_, _), SomeValue(_), NoneValue) => 1
case (Schema.Tuple2(lSchema, rSchema, _), l: Tuple, r: Tuple) => {
val leftComparison = compareBySchema(lSchema)(l.left, r.left)
if (leftComparison != 0)
leftComparison
else
compareBySchema(rSchema)(l.right, r.right)
}
case (Schema.Sequence(schema, _, _, _, _), Sequence(lVal), Sequence(rVal)) =>
compareSequences(lVal, rVal, compareBySchema(schema))
case (Schema.Fail(_, _), Error(lVal), Error(rVal)) => lVal.compareTo(rVal)
case (Schema.Transform(_, _, _, _, _), Error(lval), Error(rVal)) => lval.compareTo(rVal)
case (Schema.Transform(_, _, _, _, _), Error(_), _) => -1
case (Schema.Transform(_, _, _, _, _), _, Error(_)) => 1
case (Schema.Transform(schemaA, _, _, _, _), lVal, rVal) =>
compareBySchema(schemaA)(lVal, rVal)
case (e: Schema.Enum[_], Enumeration(_, (lField, lVal)), Enumeration(_, (rField, rVal))) if lField == rField =>
compareBySchema(e.caseOf(lField).map(_.schema).get)(lVal, rVal) // FIXME: .get
case (e: Schema.Enum[_], Enumeration(_, (lField, _)), Enumeration(_, (rField, _))) => {
val fields = e.cases.map(_.id).toList
fields.indexOf(lField).compareTo(fields.indexOf(rField))
}
//are two record with the different name equal?
case (r: Schema.Record[_], Record(_, lVals), Record(_, rVals)) =>
compareRecords(r, lVals, rVals)
case (Schema.Dynamic(_), left, right) =>
ordering(DynamicValue.schema).compare(left, right)
case _ => 0
}
private def compareRecords(
r: Schema.Record[_],
lVals: Map[String, DynamicValue],
rVals: Map[String, DynamicValue]
): Int = {
val j = r.fields.length
@tailrec
def loop(i: Int): Int =
if (i == j) 0
else {
val field = r.fields(i)
val fieldComparison = compareBySchema(field.schema)(lVals(field.name), rVals(field.name))
if (fieldComparison == 0) loop(i + 1) else fieldComparison
}
loop(0)
}
private def compareSequences(
l: Chunk[DynamicValue],
r: Chunk[DynamicValue],
f: (DynamicValue, DynamicValue) => Int
): Int = {
val j = l.length
val k = r.length
@tailrec
def loop(i: Int): Int =
if (i == j && i == k) 0
else if (i == j) -1
else if (i == k) 1
else {
val compare = f(l(i), r(i))
if (compare == 0) loop(i + 1) else compare
}
loop(0)
}
}