summaryrefslogtreecommitdiff
path: root/cask/src/cask/main/Routes.scala
blob: a4660a9935f427847af2e662ca0368d1262b2936 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package cask.main

import cask.internal.Router.EntryPoint
import cask.model.Request

import scala.reflect.macros.blackbox.Context
import language.experimental.macros

object Routes{
  case class EndpointMetadata[T](decorators: Seq[Decorator],
                                 endpoint: BaseEndpoint,
                                 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: 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
        val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator]).reverse
        if annotations.nonEmpty
      } yield {
        if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint])) c.abort(
          annotations.head.tree.pos,
          s"Last annotation applied to a function must be an instance of Endpoint, " +
          s"not ${annotations.head.tree.tpe}"
        )
        val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint])
        if(allEndpoints.length > 1) c.abort(
          annotations.head.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.head}.convertToResultType",
          tq"cask.Request",
          annotObjectSyms.map(annotObjectSym => q"$annotObjectSym.getParamParser"),
          annotObjectSyms.map(annotObjectSym => tq"$annotObjectSym.Input")

        )

        val declarations =
          for((sym, obj) <- annotObjectSyms.zip(annotObjects))
          yield q"val $sym = $obj"

        val res = q"""{
          ..$declarations
          cask.main.Routes.EndpointMetadata(
            Seq(..${annotObjectSyms.drop(1)}),
            ${annotObjectSyms.head},
            $route
          )
        }"""
        res
      }

      c.Expr[RoutesEndpointsMetadata[T]](q"""cask.main.Routes.RoutesEndpointsMetadata(..$routeParts)""")
    }
  }
}

trait Routes{

  def decorators = Seq.empty[cask.main.Decorator]
  private[this] var metadata0: Routes.RoutesEndpointsMetadata[this.type] = null
  def caskMetadata =
    if (metadata0 != null) metadata0
    else throw new Exception("Routes not yet initialize")

  protected[this] def initialize()(implicit routes: Routes.RoutesEndpointsMetadata[this.type]): Unit = {
    metadata0 = routes
  }
}