package scalaz
package http
package request
import Scalaz._
import Util.{asHashMap, mapHeads, parameters}
import Util.Nel._
sealed trait Request[IN[_]] {
val line: Line
val headers: List[(RequestHeader, NonEmptyList[Char])]
val body: IN[Byte]
import Request.request
def apply(h: RequestHeader, v: NonEmptyList[Char]) = request[IN](line, (h, v) :: headers, body)
def apply(l: Line): Request[IN] = request[IN](l, headers, body)
def apply(h: RequestHeader) = headersMapHeads.get(h)
def apply(m: Method): Request[IN] = apply(line(m))
def apply(u: Uri): Request[IN] = apply(line(u))
def apply(v: Version): Request[IN] = apply(line(v))
def >>(b: IN[Byte]) = request[IN](line, headers, b)
def |+|(b: IN[Byte])(implicit s: Semigroup[IN[Byte]]) = request[IN](line, headers, s.append(body, b))
def -(h: RequestHeader) = request[IN](line, headers filter { case (k, _) => h != k }, body)
def userAgent = this(UserAgent)
lazy val headersMap = asHashMap[List, NonEmptyList](headers)
lazy val headersMapHeads = mapHeads(headersMap)
private val m = immutableHashMapMemo[Foldable[IN], List[(List[Char], List[Char])]]
def post(implicit f: Foldable[IN]) = new {
val parameters = m(f => Util.parameters(ma(body).listl(f) map (_.toChar)))(f)
lazy val parametersMap = asHashMap[List, NonEmptyList](parameters)
lazy val parametersMapHeads = mapHeads(parametersMap)
def |(p: String) = parametersMapHeads.get(p.toList)
def ||(p: String): List[List[Char]] = parametersMap.get(p.toList)
}
def parametersMap = line.uri.parametersMap
def parametersMapHeads = line.uri.parametersMapHeads
def !(p: String) = line.uri.parametersMapHeads ∗ (_.get(p.toList))
def ^!^ : Kleisli[Option, String, List[Char]] = kleisli(this ! (_: String))
def : Validation[E, List[Char]] = this ! p toSuccess e
def !!(p: String) : List[List[Char]] = OptionNonEmptyListList(line.uri.parametersMap ∗ (_.get(p.toList)))
def ^!!^ : Kleisli[Option, String, NonEmptyList[List[Char]]] = kleisli((p : String) => {(this !! p).toNel })
def !: Validation[E, NonEmptyList[List[Char]]] = (this !! p).toNel toSuccess e
def !?(p: String) = this ! p isDefined
def ~!?(p: String) = this ! p isEmpty
def |(p: String)(implicit f: Foldable[IN]) = post | p
def ^|^(implicit f: Foldable[IN]) : Kleisli[Option, String, List[Char]] = kleisli(this | (_: String))
def |?(p: String)(implicit f: Foldable[IN]) = this | p isDefined
def ~|?(p: String)(implicit f: Foldable[IN]) = this | p isEmpty
def ||(p: String)(implicit f: Foldable[IN]) : List[List[Char]] = post || p
def ^||^(implicit f: Foldable[IN]) : Kleisli[Option, String, NonEmptyList[List[Char]]] =
kleisli((p : String) => {(this || p).toNel })
def !|(p: String)(implicit f: Foldable[IN]) = this.!(p) <+> |(p)
def ^!|^(implicit f: Foldable[IN]) : Kleisli[Option, String, List[Char]] =
kleisli((this !| (_: String)))
def |!(p: String)(implicit f: Foldable[IN]) = |(p) <+> this.!(p)
def ^|!^(implicit f: Foldable[IN]) : Kleisli[Option, String, List[Char]] = kleisli(this |! (_: String))
def !!||(p: String)(implicit f: Foldable[IN]) = this.!!(p) <+> ||(p)
def ^!!||^(implicit f: Foldable[IN]) : Kleisli[Option, String, NonEmptyList[List[Char]]] =
kleisli((p : String) => {(this !!|| p).toNel })
def ||!!(p: String)(implicit f: Foldable[IN]) = this.||(p) <+> !!(p)
def ^||!!^(implicit f: Foldable[IN]) : Kleisli[Option, String, NonEmptyList[List[Char]]] =
kleisli((p : String) => {(this ||!! p).toNel })
def method = line.method
def uri = line.uri
def version = line.version
def path = line.uri.path
def pathExtension = line.uri.pathExtension
def parts = line.uri.parts
def queryString = line.uri.queryString
def versionMajor = line.version.major
def versionMinor = line.version.minor
def path(f: NonEmptyList[Char] => Boolean) = f(line.uri.path)
def queryString(f: List[Char] => Boolean) = line.uri.queryString exists f
def pathEquals(s: String) = path(_.mkString == s)
def pathStartsWith(s: String) = path.mkString startsWith s
def queryStringEquals(s: String) = queryString(_.mkString == s)
def isOptions = line.method == OPTIONS
def isGet = line.method == GET
def isHead = line.method == HEAD
def isPost = line.method == POST
def isPut = line.method == PUT
def isDelete = line.method == DELETE
def isTrace = line.method == TRACE
def isInternetExplorer = this(UserAgent).mkString.toLowerCase contains "msie"
import response._
trait Debug[OUT[_]] {
def apply[A](f: IN[Byte] => A)(implicit e: Empty[OUT], b: Body[OUT, xml.Elem], s: Semigroup[OUT[Byte]]): Response[OUT]
}
def debug[OUT[_]] = new Debug[OUT] {
def apply[A](f: IN[Byte] => A)(implicit e: Empty[OUT], b: Body[OUT, xml.Elem], s: Semigroup[OUT[Byte]]) = {
implicit val request = Request.this
Response.emptyHeadersBodyResponse[OUT](StatusLine.statusLine[IN](OK)).xhtml <<
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Request Details</title>
</head>
<body>
<div>
{
List(("Method", method),
("URI Path", uri.path.mkString),
("URI Query String", uri.queryString map (_.mkString) getOrElse <i>N/A</i>),
("Version (major)", versionMajor.toLong),
("Version (minor)", versionMinor.toLong)) map {
case (k, v) =>
<div>{ k }</div>
<h4>{ v }</h4>
}
}
</div>
<hr/>
<div>
{
if(headers.isEmpty)
<i>N/A</i>
else
<ul>
{
headers map {
case (h, v) => <li><div><strong>{ h.asString }</strong><br/>{ v.mkString }</div></li>
}
}
</ul>
}
</div>
<hr/>
<div>
{
f(body)
}
</div>
</body>
</html>
}
}
}
object Request {
def request[IN[_]](l: Line, h: List[(RequestHeader, NonEmptyList[Char])], b: IN[Byte]) = new Request[IN] {
val line = l
val headers = h
val body = b
}
object MethodPath {
def unapply[IN[_]](r: Request[IN]): Option[(Method, String)] =
Some(r.line.method, r.line.uri.path.list.mkString)
}
object MethodUri {
def unapply[IN[_]](r: Request[IN]): Option[(Method, Uri)] =
Some(r.line.method, r.line.uri)
}
object Path {
def unapply[IN[_]](r: Request[IN]): Option[(String)] =
Some(r.line.uri.path.list.mkString)
}
object Uri {
def unapply[IN[_]](r: Request[IN]): Option[Uri] =
Some(r.line.uri)
}
object Method {
def unapply[IN[_]](r: Request[IN]): Option[Method] =
Some(r.line.method)
}
object Version {
def unapply[IN[_]](r: Request[IN]): Option[Version] =
Some(r.line.version)
}
object Parts {
def unapply[IN[_]](r : Request[IN]) : Option[List[String]] = {
Some(r.parts)
}
}
object MethodParts {
def unapply[IN[_]](r : Request[IN]) : Option[(Method, List[String])] = {
Some(r.method, r.parts)
}
}
}