Skip to content

Commit

Permalink
Merge pull request #204 from cvogt/GenLens-chains
Browse files Browse the repository at this point in the history
Enhance GenLens to support select chains i.e. _.a.b.c
  • Loading branch information
julien-truffaut committed Apr 9, 2015
2 parents f87fcab + eeaf562 commit c7ba1f3
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 10 deletions.
46 changes: 37 additions & 9 deletions macro/src/main/scala/monocle/macros/internal/Macro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,47 @@ object Macro {
}

private[macros] object MacroImpl extends MacrosCompatibility {

def genLens_impl[S: c.WeakTypeTag, A: c.WeakTypeTag](c: Context)(field: c.Expr[S => A]): c.Expr[Lens[S, A]] = {
import c.universe._
val fieldName = field match {
case Expr(
Function(
List(ValDef(_, termDefName, _, EmptyTree)),
Select(Ident(termUseName), fieldNameName))) if termDefName.decodedName.toString == termUseName.decodedName.toString =>
fieldNameName.decodedName.toString
case _ => c.abort(c.enclosingPosition, s"Illegal field reference ${show(field.tree)}; please use _.field instead")

/** Extractor for member select chains.
e.g.: SelectChain.unapply(a.b.c) == Some("a",Seq(a.type -> "b", a.b.type -> "c")) */
object SelectChain{
def unapply(tree: Tree): Option[(Name,Seq[(Type,TermName)])] = tree match {
case Select(tail@Ident(termUseName), field:TermName) =>
Some((termUseName,Seq(tail.tpe.widen -> field)))
case Select(tail, field:TermName) => SelectChain.unapply(tail).map(
t => t.copy(_2 = t._2 :+ (tail.tpe.widen -> field))
)
case _ => None
}
}

mkLens_impl[S, A](c)(c.Expr[String](q"$fieldName"))
field match {
// _.field
case Expr(
Function(
List(ValDef(_, termDefName, _, EmptyTree)),
Select(Ident(termUseName), fieldNameName)
)
) if termDefName.decodedName.toString == termUseName.decodedName.toString =>
val fieldName = fieldNameName.decodedName.toString
mkLens_impl[S, A](c)(c.Expr[String](q"$fieldName"))

// _.field1.field2...
case Expr(
Function(
List(ValDef(_, termDefName, _, EmptyTree)),
SelectChain(termUseName, typesFields)
)
) if termDefName.decodedName.toString == termUseName.decodedName.toString =>
c.Expr[Lens[S, A]](
typesFields.map{ case (t,f) => q"monocle.macros.GenLens[$t](_.$f)" }
.reduce((a,b) => q"$a composeLens $b")
)

case _ => c.abort(c.enclosingPosition, s"Illegal field reference ${show(field.tree)}; please use _.field1.field2... instead")
}
}

def mkLens_impl[S: c.WeakTypeTag, A: c.WeakTypeTag](c: Context)(fieldName: c.Expr[String]): c.Expr[Lens[S, A]] = {
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
resolvers += Resolver.sonatypeRepo("releases")

addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.3")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")

addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.5")

Expand Down
1 change: 1 addition & 0 deletions test/src/test/scala/monocle/LensSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class LensSpec extends Spec {

checkAll("apply Lens", LensLaws(_s))
checkAll("GenLens", LensLaws(GenLens[Example](_.s)))
checkAll("GenLens chain", LensLaws(GenLens[Example](_.p.x)))
checkAll("Lenses", LensLaws(Example.s))

checkAll("lens.asOptional" , OptionalLaws(_s.asOptional))
Expand Down

0 comments on commit c7ba1f3

Please sign in to comment.