summaryrefslogtreecommitdiff
path: root/cask/src/cask/main/Routes.scala
blob: ee2b2b9f3bd1b7bd1745f32982a89610cb9a094b (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
package cask.main

import cask.internal.Router
import cask.internal.Router.EntryPoint
import cask.model.{BaseResponse, ParamContext}

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

object Routes{

  trait Endpoint[R]{
    type InputType
    val path: String
    val methods: Seq[String]
    def subpath: Boolean = false
    def wrapMethodOutput(t: R): Any
    def handle(ctx: ParamContext,
               bindings: Map[String, String],
               routes: Routes,
               entryPoint: EntryPoint[InputType, Routes, cask.model.ParamContext]): Router.Result[BaseResponse]
  }

  case class EndpointMetadata[T](endpoint: Endpoint[_],
                                 entryPoint: EntryPoint[_, T, ParamContext])
  case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*)
  object RoutesEndpointsMetadata{
    implicit def initialize[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
        annot <- m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[Endpoint[_]])
      } yield {
        val annotObject = q"new ${annot.tree.tpe}(..${annot.tree.children.tail})"
        val annotObjectSym = c.universe.TermName(c.freshName("annotObject"))
        val route = router.extractMethod(
          m.asInstanceOf[MethodSymbol],
          weakTypeOf[T],
          (t: router.c.universe.Tree) => q"$annotObjectSym.wrapMethodOutput($t)",
          c.weakTypeOf[ParamContext],
          q"$annotObjectSym.parseMethodInput",
          tq"$annotObjectSym.InputType"
        )


        q"""{
          val $annotObjectSym = $annotObject
          cask.main.Routes.EndpointMetadata(
            $annotObjectSym,
            $route
          )
        }"""
      }

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

trait Routes{
  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
  }
}