Created
July 17, 2019 21:37
-
-
Save ahoy-jon/3e9d9a63f9d2141a35d5b9e79f9f1ece to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implicit val qTree:DeepEqual[QTree[Unit]] = DeepEqual.anyDeepEqual | |
//DeepEqual.gen[DescribeLong] | |
DeepEqual.gen[ParkaAnalysis].explainNotEqual(pa,paFromJson).foreach(println) | |
assert(pa == paFromJson) | |
} | |
} | |
case class Element(key: String, value: Long) | |
case class Element2(key: String, value: Long, eulav: Long) | |
trait DeepEqual[T] { | |
def explainNotEqual(t1:T,t2:T):Seq[String] | |
} | |
trait LowPriorityDeepEqual { | |
def anyDeepEqual[T]:DeepEqual[T] = new DeepEqual[T] { | |
override def explainNotEqual(t1: T, t2: T): Seq[String] = if(t1 == t2) | |
Nil | |
else Seq(s"left : $t1\nright : $t2") | |
} | |
} | |
object DeepEqual extends LowPriorityDeepEqual{ | |
implicit def str: Typeclass[String] = anyDeepEqual | |
implicit def long: Typeclass[Long] = anyDeepEqual | |
implicit def optionDeepEqual[V:DeepEqual]:DeepEqual[Option[V]] = new Typeclass[Option[V]] { | |
override def explainNotEqual(t1: Option[V], t2: Option[V]): Seq[String] = { | |
(t1, t2) match { | |
case (Some(x),Some(y)) => implicitly[DeepEqual[V]].explainNotEqual(x,y) | |
case (None,None) => Nil | |
case (Some(x), None) => Seq(s"left : extra value $x") | |
case (None, Some(y)) => Seq(s"right : extra value $y") | |
} | |
} | |
} | |
implicit def mapDeepEqual[K,V:DeepEqual]:DeepEqual[Map[K,V]] = new Typeclass[Map[K,V]] { | |
override def explainNotEqual(t1: Map[K, V], t2: Map[K, V]): Seq[String] = { | |
val keys: Seq[K] = (t1.keySet ++ t2.keySet).toSeq | |
keys.flatMap(k => { | |
(t1.get(k),t2.get(k)) match { | |
case (Some(v1),Some(v2)) => implicitly[DeepEqual[V]].explainNotEqual(v1,v2) | |
case (Some(v1), None) => Seq(s"left : extra value at key $k : $v1") | |
case (None,Some(v2)) => Seq(s"right : extra value at key $k : $v2") | |
case _ => Nil | |
} | |
}) | |
} | |
} | |
implicit def seqDeepEqual[T:DeepEqual]:DeepEqual[Seq[T]] = new Typeclass[Seq[T]] { | |
private val deepEqual = implicitly[Typeclass[T]] | |
override def explainNotEqual(t1: Seq[T], t2: Seq[T]): Seq[String] = { | |
if(t1 == t2) Nil | |
else { | |
val min = Math.min(t1.size,t2.size) | |
val max = Math.max(t1.size,t2.size) | |
val common: Seq[String] = t1.take(min).zip(t2.take(min)).flatMap({ | |
case (x,y) => | |
deepEqual.explainNotEqual(x,y) | |
}) | |
val extra: Seq[String] = if(max == min) Nil else { | |
if(t1.size > t2.size) t1.drop(min).map(x => s"left : extra element : $x") | |
else t2.drop(min).map(x => s"right : extra element : $x") | |
} | |
common ++ extra | |
} | |
} | |
} | |
def explainNotEqual[T:DeepEqual](t1:T,t2:T):Seq[String] = { | |
implicitly[DeepEqual[T]].explainNotEqual(t1,t2) | |
} | |
import magnolia._ | |
type Typeclass[T] = DeepEqual[T] | |
def combine[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] = new Typeclass[T] { | |
override def explainNotEqual(t1: T, t2: T): Seq[String] = if(t1 == t2) Nil else { | |
caseClass.parameters.flatMap(param => { | |
param.typeclass.explainNotEqual(param.dereference(t1), param.dereference(t2)) | |
}) | |
} | |
} | |
def dispatch[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] = new Typeclass[T] { | |
override def explainNotEqual(t1: T, t2: T): Seq[String] = { | |
sealedTrait.dispatch(t1)(handle1 => { | |
sealedTrait.dispatch(t2)(handle2 => { | |
if(handle1.typeName == handle2.typeName) { | |
handle1.typeclass.explainNotEqual(handle1.cast(t1),handle1.cast(t2)) | |
} else { | |
anyDeepEqual[Any].explainNotEqual(t1,t2) | |
} | |
}) | |
}) | |
} | |
} | |
implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment