Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

routeMacro refactoring #45

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions autowire/shared/src/main/scala/autowire/Internal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,36 @@ object Internal{
}
)
}


sealed trait RouteTree[PickleType] {
def isValidPath(route: Seq[String]): Boolean
def lookup(route: Seq[String]): Map[String,PickleType] => Future[PickleType]
}

class RouteNode[PickleType](children: Map[String,RouteTree[PickleType]]) extends RouteTree[PickleType] {

override def isValidPath(route: Seq[String]): Boolean = {
route.headOption.flatMap(children.get).exists(_.isValidPath(route.tail))
}

override def lookup(route: Seq[String]): Map[String,PickleType] => Future[PickleType] = {
children(route.head).lookup(route.tail)
}

override def toString = children.toString()
}

class RouteLeaf[PickleType](result: Map[String,PickleType] => Future[PickleType]) extends RouteTree[PickleType] {
override def isValidPath(route: Seq[String]): Boolean = route.isEmpty
override def lookup(route: Seq[String]): Map[String,PickleType] => Future[PickleType] = result
}

class RouterContext[PickleType](tree: RouteTree[PickleType]) {
type Router = Core.Router[PickleType]
def router: Router = {
case autowire.Core.Request(path,args) if tree.isValidPath(path) => tree.lookup(path)(args)
}
}

}
74 changes: 33 additions & 41 deletions autowire/shared/src/main/scala/autowire/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ object Macros {
def extractMethod(
pickleType: WeakTypeTag[_],
meth: MethodSymbol,
prefixPath: Seq[String],
memPath: Seq[String],
target: Expr[Any],
curCls: c.universe.Type): c.universe.Tree = {
curCls: c.universe.Type): c.Tree = {

val flattenedArgLists = meth.paramss.flatten

def hasDefault(i: Int) = {
val defaultName = s"${meth.name}$$default$$${i + 1}"
if (curCls.members.exists(_.name.toString == defaultName)) {
Expand All @@ -78,6 +79,7 @@ object Macros {
None
}
}

val argName = c.fresh[TermName]("args")
val args: Seq[Tree] = flattenedArgLists.zipWithIndex.map { case (arg, i) =>
val default = hasDefault(i) match {
Expand All @@ -90,12 +92,9 @@ object Macros {
${arg.name.toString},
${c.prefix}.read[${arg.typeSignature}](_)
)
"""
"""
}


//val memSel = c.universe.newTermName(memPath.mkString("."))

val bindings = args.foldLeft[Tree](q"Nil") { (old, next) =>
q"$next :: $old"
}
Expand All @@ -105,46 +104,39 @@ object Macros {
pq"scala.::(${next.name.toTermName}: ${next.typeSignature} @unchecked, $old)"
}



val memSel = memPath.foldLeft(q"($target)") { (cur, nex) =>
q"$cur.${c.universe.newTermName(nex)}"
}

val futurized = futurize(q"$memSel(..$nameNames)", meth)
val fullPath = prefixPath ++ memPath
val frag = cq""" autowire.Core.Request(Seq(..$fullPath), $argName) =>
autowire.Internal.doValidate($bindings) match{ case (..$assignment) =>
$futurized.map(${c.prefix}.write(_))
case _ => ???
}
"""

frag

val frag = cq"""($argName) => autowire.Internal.doValidate($bindings) match {
case (..$assignment) =>$futurized.map(${c.prefix}.write(_))
case _ => ???
}"""

q"new autowire.Internal.RouteLeaf({case $frag})"
}

def getAllRoutesForClass(
def getRouteTreeForClass(
pt: WeakTypeTag[_],
target: Expr[Any],
curCls: Type,
prefixPath: Seq[String],
memPath: Seq[String]): Iterable[c.universe.Tree] = {
//See http://stackoverflow.com/questions/15786917/cant-get-inherited-vals-with-scala-reflection
//Yep case law to program WUNDERBAR!
getValsOrMeths(curCls).flatMap {
case Left((m, t)) => Nil
//Vals / Vars
getAllRoutesForClass(pt, target, m.typeSignature, prefixPath, memPath :+ m.name.toString)
case Right((m, t)) =>
//Methods
Seq(extractMethod(pt, t, prefixPath, memPath :+ m.name.toString, target, curCls))

memPath: Seq[String]): c.Tree = {
val children: Iterable[c.Tree] = getValsOrMeths(curCls).map {
case Left((m,t)) =>
val node = getRouteTreeForClass(pt,target,m.typeSignature, memPath :+ m.name.toString)
q"""(${m.name.toString},$node)"""

case Right((m,t)) =>
val leaf = extractMethod(pt,t,memPath :+ m.name.toString,target,curCls)
q"""(${m.name.toString},$leaf)"""
}
q"new autowire.Internal.RouteNode(Map(..$children))"
}

}


def clientMacro[Result]
(c: Context)
()
Expand Down Expand Up @@ -271,20 +263,20 @@ object Macros {
}

def routeMacro[Trait, PickleType]
(c: Context)
(target: c.Expr[Trait])
(implicit t: c.WeakTypeTag[Trait], pt: c.WeakTypeTag[PickleType])
: c.Expr[Router[PickleType]] = {
(c: Context)
(target: c.Expr[Trait])
(implicit t: c.WeakTypeTag[Trait], pt: c.WeakTypeTag[PickleType])
: c.Expr[Router[PickleType]] = {
import c.universe._
val help = new MacroHelp[c.type](c)
val topClass = weakTypeOf[Trait]
val routes = help.getAllRoutesForClass(pt, target, topClass, topClass.typeSymbol.fullName.toString.split('.').toSeq, Nil).toList

val res = q"{case ..$routes}: autowire.Core.Router[$pt]"
// println("RES", res)
val partial = help.getRouteTreeForClass(pt,target,topClass,Nil)
val routes = topClass.typeSymbol.fullName.toString.split('.').reverse.foldLeft(partial) {
case (acc,prefix) => q"new autowire.Internal.RouteNode(Map($prefix->$acc))"
}
val res = q"""new autowire.Internal.RouterContext[$pt]($routes).router"""
c.Expr(res)
}


}

}
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ crossScalaVersions := Seq("2.10.4", "2.11.4")
val autowire = crossProject.settings(
organization := "com.lihaoyi",

version := "0.2.5",
version := "0.2.6-SNAPSHOT",
name := "autowire",
scalaVersion := "2.10.4",
scalaVersion := "2.11.7",
autoCompilerPlugins := true,
addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2"),
libraryDependencies ++= Seq(
Expand Down
2 changes: 1 addition & 1 deletion project/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5")