summaryrefslogtreecommitdiff
path: root/cask/src/cask/router/EntryPoint.scala
blob: 6fe44fc5343ccf636b19c2c051c95e6e2b6d4c7a (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
package cask.router


import scala.collection.mutable


/**
 * What is known about a single endpoint for our routes. It has a [[name]],
 * [[argSignatures]] for each argument, and a macro-generated [[invoke0]]
 * that performs all the necessary argument parsing and de-serialization.
 *
 * Realistically, you will probably spend most of your time calling [[invoke]]
 * instead, which provides a nicer API to call it that mimmicks the API of
 * calling a Scala method.
 */
case class EntryPoint[T, C](name: String,
                            argSignatures: Seq[Seq[ArgSig[_, T, _, C]]],
                            doc: Option[String],
                            invoke0: (T, C, Seq[Map[String, Any]], Seq[Seq[ArgSig[Any, _, _, C]]]) => Result[Any]){

  val firstArgs = argSignatures.head
    .map(x => x.name -> x)
    .toMap[String, ArgSig[_, T, _, C]]

  def invoke(target: T,
             ctx: C,
             paramLists: Seq[Map[String, Any]]): Result[Any] = {

    val missing = mutable.Buffer.empty[ArgSig[_, T, _, C]]

    val unknown = paramLists.head.keys.filter(!firstArgs.contains(_))

    for(k <- firstArgs.keys) {
      if (!paramLists.head.contains(k)) {
        val as = firstArgs(k)
        if (as.reads.arity != 0 && as.default.isEmpty) missing.append(as)
      }
    }

    if (missing.nonEmpty || unknown.nonEmpty) Result.Error.MismatchedArguments(missing.toSeq, unknown.toSeq)
    else {
      try invoke0(
        target,
        ctx,
        paramLists,
        argSignatures.asInstanceOf[Seq[Seq[ArgSig[Any, _, _, C]]]]
      )
      catch{case e: Throwable => Result.Error.Exception(e)}
    }
  }
}