From 0e098a93da94c251eb05d42bc7ef48394600508c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 15 Sep 2019 12:36:39 +0800 Subject: Break circular dependency by splitting `RouteEndpointMetadata` into its own file --- cask/src/cask/main/RoutesEndpointMetadata.scala | 68 +++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 cask/src/cask/main/RoutesEndpointMetadata.scala (limited to 'cask/src/cask/main/RoutesEndpointMetadata.scala') diff --git a/cask/src/cask/main/RoutesEndpointMetadata.scala b/cask/src/cask/main/RoutesEndpointMetadata.scala new file mode 100644 index 0000000..fa93a0c --- /dev/null +++ b/cask/src/cask/main/RoutesEndpointMetadata.scala @@ -0,0 +1,68 @@ +package cask.main + +import cask.internal.Router.EntryPoint + +import language.experimental.macros +import scala.reflect.macros.blackbox +case class EndpointMetadata[T](decorators: Seq[RawDecorator], + endpoint: Endpoint[_, _], + entryPoint: EntryPoint[T, _]) +case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*) +object RoutesEndpointsMetadata{ + implicit def initialize[T]: RoutesEndpointsMetadata[T] = macro initializeImpl[T] + implicit def initializeImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[RoutesEndpointsMetadata[T]] = { + import c.universe._ + val router = new cask.internal.Router[c.type](c) + + val routeParts = for{ + m <- c.weakTypeOf[T].members + annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[Decorator[_, _]]) + if annotations.nonEmpty + } yield { + if(!(annotations.last.tree.tpe <:< weakTypeOf[Endpoint[_, _]])) c.abort( + annotations.head.tree.pos, + s"Last annotation applied to a function must be an instance of Endpoint, " + + s"not ${annotations.last.tree.tpe}" + ) + val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[Endpoint[_, _]]) + if(allEndpoints.length > 1) c.abort( + annotations.last.tree.pos, + s"You can only apply one Endpoint annotation to a function, not " + + s"${allEndpoints.length} in ${allEndpoints.map(_.tree.tpe).mkString(", ")}" + ) + + val annotObjects = + for(annot <- annotations) + yield q"new ${annot.tree.tpe}(..${annot.tree.children.tail})" + + val annotObjectSyms = + for(_ <- annotations.indices) + yield c.universe.TermName(c.freshName("annotObject")) + + val route = router.extractMethod( + m.asInstanceOf[MethodSymbol], + weakTypeOf[T], + q"${annotObjectSyms.last}.convertToResultType", + tq"cask.Request", + annotObjectSyms.reverse.map(annotObjectSym => q"$annotObjectSym.getParamParser"), + annotObjectSyms.reverse.map(annotObjectSym => tq"$annotObjectSym.InputTypeAlias") + ) + + val declarations = + for((sym, obj) <- annotObjectSyms.zip(annotObjects)) + yield q"val $sym = $obj" + + val res = q"""{ + ..$declarations + cask.main.EndpointMetadata( + Seq(..${annotObjectSyms.dropRight(1)}), + ${annotObjectSyms.last}, + $route + ) + }""" + res + } + + c.Expr[RoutesEndpointsMetadata[T]](q"""cask.main.RoutesEndpointsMetadata(..$routeParts)""") + } +} \ No newline at end of file -- cgit v1.2.3