Skip to content

Commit c28f318

Browse files
feat: add eqv derivation with fields to compare (#415)
1 parent 7532374 commit c28f318

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

modules/cats/src/main/scala/derevo/cats/eqv.scala

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package derevo.cats
22

33
import cats.Eq
4+
import derevo.{Derivation, NewTypeDerivation}
45
import magnolia.{CaseClass, Magnolia, SealedTrait}
5-
import derevo.Derivation
6-
import derevo.NewTypeDerivation
6+
7+
import scala.reflect.macros.blackbox
78

89
object eqv extends Derivation[Eq] with NewTypeDerivation[Eq] {
910
type Typeclass[T] = Eq[T]
@@ -25,6 +26,31 @@ object eqv extends Derivation[Eq] with NewTypeDerivation[Eq] {
2526

2627
implicit def instance[T]: Eq[T] = macro Magnolia.gen[T]
2728

29+
def apply[T](eqFields: String*): Eq[T] = macro eqExtendedImpl[T]
30+
31+
def eqExtendedImpl[T: c.WeakTypeTag](c: blackbox.Context)(eqFields: c.Expr[String]*): c.Tree = {
32+
import c.universe._
33+
34+
val T = weakTypeOf[T]
35+
val eqFieldsSet = eqFields.map(_.tree).collect {
36+
case Literal(Constant(field: String)) => field
37+
}.toSet
38+
39+
val comparisons = T.decls.collect {
40+
case m: MethodSymbol if m.isCaseAccessor && eqFieldsSet.contains(m.name.toString) =>
41+
val name = m.name.toTermName
42+
q"Eq[${m.typeSignature}].eqv(x.$name, y.$name)"
43+
}
44+
45+
q"""
46+
new Eq[$T] {
47+
def eqv(x: $T, y: $T): Boolean = {
48+
..$comparisons
49+
}
50+
}
51+
"""
52+
}
53+
2854
object universal extends Derivation[Eq] {
2955
implicit def instance[T]: Eq[T] = Eq.fromUniversalEquals[T]
3056
}

modules/cats/src/test/scala/derevo/cats/EqSpec.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ class EqSpec extends AnyFreeSpec {
3434
assert(Eq[Qux].eqv(Qux(1), Qux(1)))
3535
assert(Eq[Qux].neqv(Qux(1), Qux(-1)))
3636
}
37+
38+
"with ignoring fields" in {
39+
@derive(eqv("bar"))
40+
case class Foo(bar: Int, baz: Int)
41+
42+
assert(Eq[Foo].eqv(Foo(1, 2), Foo(1, 5)))
43+
assert(Eq[Foo].neqv(Foo(2, 2), Foo(3, 2)))
44+
assert(Eq[Foo].neqv(Foo(2, 2), Foo(3, 1)))
45+
}
3746
}
3847
}
3948
}

0 commit comments

Comments
 (0)