Skip to content

Commit

Permalink
0.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi committed Aug 18, 2018
1 parent f8bb6f6 commit 4b0cfdf
Show file tree
Hide file tree
Showing 43 changed files with 139 additions and 177 deletions.
2 changes: 1 addition & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ object cask extends ScalaModule with PublishModule {
}
object example extends Module{
trait LocalModule extends ScalaModule{
def ivyDeps = super.ivyDeps().filter(_ != ivy"com.lihaoyi::cask:0.1.0")
def ivyDeps = super.ivyDeps().filter(_ != ivy"com.lihaoyi::cask:0.1.1")

override def millSourcePath = super.millSourcePath / "app"
def moduleDeps = Seq(cask)
Expand Down
4 changes: 2 additions & 2 deletions cask/src/cask/decorators/compress.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import java.io.{ByteArrayOutputStream, OutputStream}
import java.util.zip.{DeflaterOutputStream, GZIPOutputStream}

import cask.internal.Router
import cask.model.{ParamContext, Response}
import cask.model.{Request, Response}

import collection.JavaConverters._
class compress extends cask.Decorator{
def wrapFunction(ctx: ParamContext, delegate: Delegate) = {
def wrapFunction(ctx: Request, delegate: Delegate) = {
val acceptEncodings = ctx.exchange.getRequestHeaders.get("Accept-Encoding").asScala.flatMap(_.split(", "))
delegate(Map()).map{ v =>
val (newData, newHeaders) = if (acceptEncodings.exists(_.toLowerCase == "gzip")) {
Expand Down
18 changes: 9 additions & 9 deletions cask/src/cask/endpoints/FormEndpoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,40 @@ import io.undertow.server.handlers.form.FormParserFactory

import collection.JavaConverters._

sealed trait FormReader[T] extends Router.ArgReader[Seq[FormEntry], T, ParamContext]
sealed trait FormReader[T] extends Router.ArgReader[Seq[FormEntry], T, Request]
object FormReader{
implicit def paramFormReader[T: QueryParamReader] = new FormReader[T]{
def arity = implicitly[QueryParamReader[T]].arity

def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = {
def read(ctx: Request, label: String, input: Seq[FormEntry]) = {
implicitly[QueryParamReader[T]].read(ctx, label, if (input == null) null else input.map(_.valueOrFileName))
}
}

implicit def formEntryReader = new FormReader[FormEntry]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head
}
implicit def formEntriesReader = new FormReader[Seq[FormEntry]]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input
}

implicit def formValueReader = new FormReader[FormValue]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormValue]
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormValue]
}
implicit def formValuesReader = new FormReader[Seq[FormValue]]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormValue])
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormValue])
}
implicit def formFileReader = new FormReader[FormFile]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormFile]
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormFile]
}
implicit def formFilesReader = new FormReader[Seq[FormFile]]{
def arity = 1
def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile])
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile])
}
}
class postForm(val path: String, override val subpath: Boolean = false) extends Endpoint {
Expand All @@ -49,7 +49,7 @@ class postForm(val path: String, override val subpath: Boolean = false) extends
val methods = Seq("post")
type Input = Seq[FormEntry]
type InputParser[T] = FormReader[T]
def wrapFunction(ctx: ParamContext,
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
try {
val formData = FormParserFactory.builder().build().createParser(ctx.exchange).parseBlocking()
Expand Down
10 changes: 5 additions & 5 deletions cask/src/cask/endpoints/JsonEndpoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import java.io.ByteArrayOutputStream
import cask.internal.{Router, Util}
import cask.internal.Router.EntryPoint
import cask.main.{Endpoint, HttpDecorator, Routes}
import cask.model.{ParamContext, Response}
import cask.model.{Request, Response}


sealed trait JsReader[T] extends Router.ArgReader[ujson.Js.Value, T, cask.model.ParamContext]
sealed trait JsReader[T] extends Router.ArgReader[ujson.Js.Value, T, cask.model.Request]
object JsReader{
implicit def defaultJsReader[T: upickle.default.Reader] = new JsReader[T]{
def arity = 1

def read(ctx: cask.model.ParamContext, label: String, input: ujson.Js.Value): T = {
def read(ctx: cask.model.Request, label: String, input: ujson.Js.Value): T = {
implicitly[upickle.default.Reader[T]].apply(input)
}
}

implicit def paramReader[T: ParamReader] = new JsReader[T] {
override def arity = 0

override def read(ctx: cask.model.ParamContext, label: String, v: ujson.Js.Value) = {
override def read(ctx: cask.model.Request, label: String, v: ujson.Js.Value) = {
implicitly[ParamReader[T]].read(ctx, label, Nil)
}
}
Expand All @@ -31,7 +31,7 @@ class postJson(val path: String, override val subpath: Boolean = false) extends
val methods = Seq("post")
type Input = ujson.Js.Value
type InputParser[T] = JsReader[T]
def wrapFunction(ctx: ParamContext,
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
val obj = for{
str <-
Expand Down
16 changes: 11 additions & 5 deletions cask/src/cask/endpoints/ParamReader.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
package cask.endpoints

import cask.internal.Router
import cask.model.ParamContext
import cask.model.{Cookie, Request}
import io.undertow.server.HttpServerExchange
import io.undertow.server.handlers.form.{FormData, FormParserFactory}

abstract class ParamReader[T] extends Router.ArgReader[Unit, T, cask.model.ParamContext]{
abstract class ParamReader[T] extends Router.ArgReader[Unit, T, cask.model.Request]{
def arity: Int
def read(ctx: cask.model.ParamContext, label: String, v: Unit): T
def read(ctx: cask.model.Request, label: String, v: Unit): T
}
object ParamReader{
class NilParam[T](f: (ParamContext, String) => T) extends ParamReader[T]{
class NilParam[T](f: (Request, String) => T) extends ParamReader[T]{
def arity = 0
def read(ctx: cask.model.ParamContext, label: String, v: Unit): T = f(ctx, label)
def read(ctx: cask.model.Request, label: String, v: Unit): T = f(ctx, label)
}
implicit object HttpExchangeParam extends NilParam[HttpServerExchange]((ctx, label) => ctx.exchange)

implicit object FormDataParam extends NilParam[FormData]((ctx, label) =>
FormParserFactory.builder().build().createParser(ctx.exchange).parseBlocking()
)

implicit object RequestParam extends NilParam[Request]((ctx, label) => ctx)

implicit object CookieParam extends NilParam[Cookie]((ctx, label) =>
Cookie.fromUndertow(ctx.exchange.getRequestCookies().get(label))
)
}
10 changes: 5 additions & 5 deletions cask/src/cask/endpoints/StaticEndpoints.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package cask.endpoints

import cask.main.Endpoint
import cask.model.ParamContext
import cask.model.Request

class staticFiles(val path: String) extends Endpoint{
type Output = String
val methods = Seq("get")
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = {
delegate(Map()).map(t => cask.model.StaticFile(t + "/" + ctx.remaining.mkString("/")))
def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
delegate(Map()).map(t => cask.model.StaticFile(t + "/" + ctx.remainingPathSegments.mkString("/")))
}

def wrapPathSegment(s: String): Input = Seq(s)
Expand All @@ -22,9 +22,9 @@ class staticResources(val path: String, resourceRoot: ClassLoader = getClass.get
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = {
def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
delegate(Map()).map(t =>
cask.model.StaticResource(t + "/" + ctx.remaining.mkString("/"), resourceRoot)
cask.model.StaticResource(t + "/" + ctx.remainingPathSegments.mkString("/"), resourceRoot)
)
}

Expand Down
16 changes: 8 additions & 8 deletions cask/src/cask/endpoints/WebEndpoints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cask.endpoints

import cask.internal.Router
import cask.main.{Endpoint, HttpDecorator}
import cask.model.{ParamContext, Response}
import cask.model.{Request, Response}

import collection.JavaConverters._

Expand All @@ -11,7 +11,7 @@ trait WebEndpoint extends Endpoint with HttpDecorator{
type Output = Response
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
def wrapFunction(ctx: ParamContext,
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
delegate(
ctx.exchange.getQueryParameters
Expand All @@ -34,14 +34,14 @@ class put(val path: String, override val subpath: Boolean = false) extends WebEn
class route(val path: String, val methods: Seq[String], override val subpath: Boolean = false) extends WebEndpoint

abstract class QueryParamReader[T]
extends Router.ArgReader[Seq[String], T, cask.model.ParamContext]{
extends Router.ArgReader[Seq[String], T, cask.model.Request]{
def arity: Int
def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T
def read(ctx: cask.model.Request, label: String, v: Seq[String]): T
}
object QueryParamReader{
class SimpleParam[T](f: String => T) extends QueryParamReader[T]{
def arity = 1
def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T = f(v.head)
def read(ctx: cask.model.Request, label: String, v: Seq[String]): T = f(v.head)
}

implicit object StringParam extends SimpleParam[String](x => x)
Expand All @@ -54,20 +54,20 @@ object QueryParamReader{
implicit object FloatParam extends SimpleParam[Float](_.toFloat)
implicit def SeqParam[T: QueryParamReader] = new QueryParamReader[Seq[T]]{
def arity = 1
def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): Seq[T] = {
def read(ctx: cask.model.Request, label: String, v: Seq[String]): Seq[T] = {
v.map(x => implicitly[QueryParamReader[T]].read(ctx, label, Seq(x)))
}
}
implicit def OptionParam[T: QueryParamReader] = new QueryParamReader[Option[T]]{
def arity = 1
def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): Option[T] = {
def read(ctx: cask.model.Request, label: String, v: Seq[String]): Option[T] = {
v.headOption.map(x => implicitly[QueryParamReader[T]].read(ctx, label, Seq(x)))
}
}
implicit def paramReader[T: ParamReader] = new QueryParamReader[T] {
override def arity = 0

override def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]) = {
override def read(ctx: cask.model.Request, label: String, v: Seq[String]) = {
implicitly[ParamReader[T]].read(ctx, label, v)
}
}
Expand Down
15 changes: 6 additions & 9 deletions cask/src/cask/endpoints/WebSocketEndpoint.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
package cask.endpoints

import cask.internal.Router
import cask.model.{ParamContext, Subpath}
import cask.model.Request
import io.undertow.server.HttpServerExchange
import io.undertow.websockets.WebSocketConnectionCallback
trait WebsocketParam[T] extends Router.ArgReader[Seq[String], T, cask.model.ParamContext]
trait WebsocketParam[T] extends Router.ArgReader[Seq[String], T, cask.model.Request]

object WebsocketParam{
class NilParam[T](f: (ParamContext, String) => T) extends WebsocketParam[T]{
class NilParam[T](f: (Request, String) => T) extends WebsocketParam[T]{
def arity = 0
def read(ctx: ParamContext, label: String, v: Seq[String]): T = f(ctx, label)
def read(ctx: Request, label: String, v: Seq[String]): T = f(ctx, label)
}
implicit object HttpExchangeParam extends NilParam[HttpServerExchange](
(ctx, label) => ctx.exchange
)
implicit object SubpathParam extends NilParam[Subpath](
(ctx, label) => new Subpath(ctx.remaining)
)
class SimpleParam[T](f: String => T) extends WebsocketParam[T]{
def arity = 1
def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T = f(v.head)
def read(ctx: cask.model.Request, label: String, v: Seq[String]): T = f(v.head)
}

implicit object StringParam extends SimpleParam[String](x => x)
Expand All @@ -44,7 +41,7 @@ class websocket(val path: String, override val subpath: Boolean = false) extends
type Input = Seq[String]
type InputParser[T] = WebsocketParam[T]
type Returned = Router.Result[WebsocketResult]
def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = delegate(Map())
def wrapFunction(ctx: Request, delegate: Delegate): Returned = delegate(Map())

def wrapPathSegment(s: String): Input = Seq(s)

Expand Down
13 changes: 9 additions & 4 deletions cask/src/cask/internal/DispatchTrie.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,20 @@ object DispatchTrie{
}else{
DispatchTrie[T](
current = terminals.headOption.map(x => x._2 -> x._3),
children = continuations.map{ case (k, vs) =>
if (!k.startsWith("::")) (k, construct(index + 1, vs))
else (k, DispatchTrie(Some(vs.head._2 -> vs.head._3), Map()))
}.toMap
children = continuations.map{ case (k, vs) => (k, construct(index + 1, vs))}.toMap
)
}
}
}

/**
* A simple Trie that can be compiled from a list of endpoints, to allow
* endpoint lookup in O(length-of-request-path) time. Lookup returns the
* [[T]] this trie contains, as well as a map of bound wildcards (path
* segments starting with `:`) and any remaining un-used path segments
* (only when `current._2 == true`, indicating this route allows trailing
* segments)
*/
case class DispatchTrie[T](current: Option[(T, Boolean)],
children: Map[String, DispatchTrie[T]]){
final def lookup(remainingInput: List[String],
Expand Down
10 changes: 5 additions & 5 deletions cask/src/cask/main/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cask.main

import cask.internal.Router
import cask.internal.Router.ArgReader
import cask.model.{ParamContext, Response}
import cask.model.{Request, Response}


trait Endpoint extends BaseEndpoint with HttpDecorator
Expand Down Expand Up @@ -44,11 +44,11 @@ trait BaseEndpoint extends BaseDecorator{

trait BaseDecorator{
type Input
type InputParser[T] <: ArgReader[Input, T, ParamContext]
type InputParser[T] <: ArgReader[Input, T, Request]
type Output
type Delegate = Map[String, Input] => Router.Result[Output]
type Returned <: Router.Result[Any]
def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned
def wrapFunction(ctx: Request, delegate: Delegate): Returned
def getParamParser[T](implicit p: InputParser[T]) = p
}
trait HttpDecorator extends BaseDecorator{
Expand All @@ -73,10 +73,10 @@ trait Decorator extends HttpDecorator {
type InputParser[T] = NoOpParser[Input, T]
}

class NoOpParser[Input, T] extends ArgReader[Input, T, ParamContext] {
class NoOpParser[Input, T] extends ArgReader[Input, T, Request] {
def arity = 1

def read(ctx: ParamContext, label: String, input: Input) = input.asInstanceOf[T]
def read(ctx: Request, label: String, input: Input) = input.asInstanceOf[T]
}
object NoOpParser{
implicit def instance[Input, T] = new NoOpParser[Input, T]
Expand Down
4 changes: 2 additions & 2 deletions cask/src/cask/main/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ abstract class BaseMain{
case None =>
writeResponse(exchange, handleNotFound())
case Some(((routes, metadata), extBindings, remaining)) =>
val ctx = ParamContext(exchange, remaining)
val ctx = Request(exchange, remaining)
def rec(remaining: List[Decorator],
bindings: List[Map[String, Any]]): Router.Result[Any] = try {
remaining match {
Expand All @@ -84,7 +84,7 @@ abstract class BaseMain{
case Nil =>
metadata.endpoint.wrapFunction(ctx, epBindings =>
metadata.entryPoint
.asInstanceOf[EntryPoint[cask.main.Routes, cask.model.ParamContext]]
.asInstanceOf[EntryPoint[cask.main.Routes, cask.model.Request]]
.invoke(routes, ctx, (epBindings ++ extBindings.mapValues(metadata.endpoint.wrapPathSegment)) :: bindings.reverse)
.asInstanceOf[Router.Result[Nothing]]
)
Expand Down
4 changes: 2 additions & 2 deletions cask/src/cask/main/Routes.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cask.main

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

import scala.reflect.macros.blackbox.Context
import language.experimental.macros
Expand Down Expand Up @@ -43,7 +43,7 @@ object Routes{
m.asInstanceOf[MethodSymbol],
weakTypeOf[T],
q"${annotObjectSyms.head}.convertToResultType",
tq"cask.ParamContext",
tq"cask.Request",
annotObjectSyms.map(annotObjectSym => q"$annotObjectSym.getParamParser"),
annotObjectSyms.map(annotObjectSym => tq"$annotObjectSym.Input")

Expand Down
5 changes: 0 additions & 5 deletions cask/src/cask/model/ParamContext.scala

This file was deleted.

Loading

0 comments on commit 4b0cfdf

Please sign in to comment.