Full Code of pheymann/typedapi for AI

master c13558a527f2 cached
81 files
247.9 KB
71.5k tokens
1 requests
Download .txt
Showing preview only (275K chars total). Download the full file or copy to clipboard to get everything.
Repository: pheymann/typedapi
Branch: master
Commit: c13558a527f2
Files: 81
Total size: 247.9 KB

Directory structure:
gitextract_555nqngw/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── akka-http-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── akkahttp/
│                           └── package.scala
├── akka-http-server/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       └── akkahttp/
│                           └── package.scala
├── ammonite-client-support/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── package.scala
├── build.sbt
├── client/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── typedapi/
│       │           └── client/
│       │               ├── ApiRequest.scala
│       │               ├── ClientManager.scala
│       │               ├── ExecutableDerivation.scala
│       │               ├── ExecutablesFromHList.scala
│       │               ├── FilterServerElements.scala
│       │               ├── RequestDataBuilder.scala
│       │               ├── package.scala
│       │               └── test/
│       │                   ├── RequestInput.scala
│       │                   └── package.scala
│       └── test/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       ├── ClientManagerSpec.scala
│                       └── RequestDataBuilderSpec.scala
├── docs/
│   ├── ApiDefinition.md
│   ├── ClientCreation.md
│   ├── ExtendIt.md
│   ├── ServerCreation.md
│   └── example/
│       ├── ammonite_client_example.sc
│       ├── build.sbt
│       ├── client-js/
│       │   ├── index.html
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Client.scala
│       ├── client-jvm/
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Client.scala
│       ├── project/
│       │   ├── build.properties
│       │   └── plugins.sbt
│       ├── server/
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Server.scala
│       └── shared/
│           └── src/
│               └── main/
│                   └── scala/
│                       ├── Apis.scala
│                       └── User.scala
├── http-support-tests/
│   └── src/
│       └── test/
│           └── scala/
│               └── http/
│                   └── support/
│                       └── tests/
│                           ├── User.scala
│                           ├── client/
│                           │   ├── AkkaHttpClientSupportSpec.scala
│                           │   ├── Http4sClientSupportSpec.scala
│                           │   ├── ScalajHttpClientSupportSpec.scala
│                           │   └── TestServer.scala
│                           ├── package.scala
│                           └── server/
│                               ├── AkkaHttpServerSupportSpec.scala
│                               ├── Http4sServerSupportSpec.scala
│                               └── ServerSupportSpec.scala
├── http4s-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── http4s/
│                           └── package.scala
├── http4s-server/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       └── htt4ps/
│                           └── package.scala
├── js-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── js/
│                           └── package.scala
├── project/
│   ├── build.properties
│   ├── build.scala
│   └── plugins.sbt
├── scalaj-http-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── scalajhttp/
│                           └── package.scala
├── server/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── typedapi/
│       │           └── server/
│       │               ├── Endpoint.scala
│       │               ├── EndpointComposition.scala
│       │               ├── EndpointExecutor.scala
│       │               ├── FilterClientElements.scala
│       │               ├── RouteExtractor.scala
│       │               ├── Serve.scala
│       │               ├── ServeToList.scala
│       │               ├── ServerHeaderExtractor.scala
│       │               ├── ServerManager.scala
│       │               ├── StatusCodes.scala
│       │               └── package.scala
│       └── test/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       ├── ApiToEndpointLinkSpec.scala
│                       ├── RouteExtractorSpec.scala
│                       └── ServeAndMountSpec.scala
└── shared/
    └── src/
        ├── main/
        │   └── scala/
        │       └── typedapi/
        │           ├── dsl/
        │           │   ├── ApiDsl.scala
        │           │   └── package.scala
        │           ├── package.scala
        │           ├── shared/
        │           │   ├── ApiElement.scala
        │           │   ├── ApiList.scala
        │           │   ├── ApiTransformer.scala
        │           │   ├── MediaTypes.scala
        │           │   ├── TypeCarrier.scala
        │           │   ├── TypeLevelFoldLeft.scala
        │           │   ├── WitnessToString.scala
        │           │   └── package.scala
        │           └── util/
        │               ├── Decoder.scala
        │               └── Encoder.scala
        └── test/
            └── scala/
                └── typedapi/
                    ├── ApiDefinitionSpec.scala
                    ├── SpecUtil.scala
                    ├── dsl/
                    │   └── ApiDslSpec.scala
                    └── shared/
                        └── ApiTransformerSpec.scala

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# ensime
*.*~
.ensime
.ensime_cache
project/ensime-plugin.sbt

# sbt
target/
project/target/
project/project/

================================================
FILE: .travis.yml
================================================
language: scala

scala:
  - 2.11.11
  - 2.12.3

jdk:
  - oraclejdk8

script:
  - sbt ++$TRAVIS_SCALA_VERSION clean test
  - sbt "project sharedJS" "fastOptJS"
  - sbt "project js-client" "fastOptJS"


================================================
FILE: CHANGELOG.md
================================================
### 0.2.0
 - `StatusCodes` and `MediaTypes` are in distinct `object`s, thus, have to be imported explicitly
 - improved Ammonite integration
 - internal: separated decoded and raw requests with `RawApiRequest` and `ApiRequest`
 - fixed `implicitNotFound` message for `ApiRequest`
 - implemented [#31](https://github.com/pheymann/typedapi/issues/31) [#35](https://github.com/pheymann/typedapi/issues/35) [#36](https://github.com/pheymann/typedapi/issues/36)
 - server endpoints have to define a response code:
 ```Scala
 derive[IO](Api) { input =>
   success(/*...*/)
      ^
 }
 ```
 

### 0.1.0
 - internal cleanups and refactorings
 - extended example project and added ScalaJS client
 - centralized http-support specs
 - added akka-http support on server and client-side
 - added scalaj-http support on the client-side
 - added ScalaJS compilation support for shared and client code
 - implemented basic ScalaJS client
 - added body encoding types and made them mandatory (several hundred Mediatypes supported)
   ```Scala
   := :> ReqBody[Json, User] :> Get[Json, User]
    _______________^__________________^
   ```
 
 - `RawHeaders` was removed
 - fixed headers were added; a fixed header is a statically known key-value pair, therefore, no input is required
   ```Scala
   // dsl
   := :> Header("Access-Control-Allow-Origin", "*") :> Get[Json, User]
   
   // function
   api(Get[Json, User], headers = Headers add("Access-Control-Allow-Origin", "*"))
   ```
   
 - changes to the server API:
   - `NoReqBodyExecutor` and `ReqBodyExecutor` now expect a `MethodType`:
   ```Scala
   new NoReqBodyExecutor[El, KIn, VIn, M, F, FOut] {
   ____________________________________^
  
   new ReqBodyExecutor[El, KIn, VIn, Bd, M, ROut, POut, F, FOut] {
   ______________________________________^
   ```
   
   - fixed header only sent by the server
   ```Scala
   := :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]
   
   api(Get[Json, User], Headers.serverSend("Access-Control-Allow-Origin", "*"))
   ```
   - extract headers which have keys that match a `String`
   ```Scala
   := :> Server.Match[String]("Control") :> Get[Json, User]
   
   api(Get[Json, User], Headers.serverMatch[String]("Control"))
   ```
 - changes to the client API:
   - new encoding types add `Content-Type` and `Accept` headers
   - fixed header only sent by the client
   ```Scala
   := :> Client.Header("Access-Control-Allow-Origin", "*") :> Get[Json, User]
   
   api(Get[Json, User], Headers.client("Access-Control-Allow-Origin", "*"))
   ```
   - send dynamic header ignore it on the server-side
   ```Scala
   := :> Client.Header[String]("Foo") :> Get[Json, User]
   
   api(Get[Json, User], Headers.client[String]("Foo"))
   ```

### 0.1.0-RC5 / Almost there
 - changes to the client API:
 ```Scala
 val ApiList =
   (:= :> Get[Foo]) :|:
   (:= :> RequestBody[Foo] :> Put[Foo])
   
 // `:|:` removed for API compositions
 val (get, put) = deriveAll(ApiList)
 ```
 
 - changes to the server API:
 ```Scala
 // same for endpoint compositions
 val e = deriveAll[IO](ApiList).from(get, put)
 ```

### 0.1.0-RC4 / Towards a stable API
 - changes to the client API:
 ```Scala
 val Api     = := :> Get[Foo]
 val ApiList =
   (:= :> Get[Foo]) :|:
   (:= :> RequestBody[Foo] :> Put[Foo])
 
 // not `compile`, but
 val foo = derive(Api)
 
 val (foo2 :|: bar :|: =:) = deriveAll(ApiList)
 
 ...
 // explicitly pass ClientManager
 foo().run[IO](cm)
 _______________^
 ```
 
 - changes to the server API
 ```Scala
 // not `link.to`, but
 val endpoint = derive[IO](Api).from(...)
 
 val endpoints = deriveAll[IO](ApiList).from(...)
 ```
 
 - major changes were applied to the internal code to reach a stable state (see [this PR](https://github.com/pheymann/typedapi/pull/13))


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Paul Heymann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[![Build Status](https://travis-ci.org/pheymann/typedapi.svg?branch=master)](https://travis-ci.org/pheymann/typedapi)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.pheymann/typedapi-client_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.pheymann/typedapi-shared_2.12)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pheymann/Lobby)
[![Scala.js](https://www.scala-js.org/assets/badges/scalajs-0.6.17.svg)](https://www.scala-js.org/)

*experimental project*: see issues [#39](https://github.com/pheymann/typedapi/issues/39) and [#41](https://github.com/pheymann/typedapi/issues/41)

# Typedapi
Define type safe APIs and let the Scala compiler do the rest:

### Api definition
```Scala
import typedapi._

val MyApi =
  // GET {body: User} /fetch/user?{name: String}
  api(method = Get[MT.`application/json`, User], 
      path = Root / "fetch" / "user", 
      queries = Queries add Query[String]('name)) :|:
  // POST {body: User} /create/user
  apiWithBody(method = Post[MT.`application/json`, User], 
              body = ReqBody[Json, User], 
              path = Root / "create" / "user")
```

And for the Servant lovers:

```Scala
import typedapi.dsl._

val MyApi = 
  // GET {body: User} /fetch/user?{name: String}
  (:= :> "fetch" :> "user" :> Query[String]('name) :> Get[MT.`application/json`, User]) :|:
  // POST {body: User} /create/user
  (:= :> "create" :> "user" :> ReqBody[Json, User] :> Post[MT.`application/json`, User])
```

### Client side
```Scala
import typedapi.client._

val (fetch, create) = deriveAll(MyApi)

import typedapi.client.http4s._; import cats.effect.IO; import org.http4s.client.blaze.Http1Client

val cm = ClientManager(Http1Client[IO]().unsafeRunSync, "http://my-host", 8080)

fetch("joe").run[IO](cm): IO[User]
```

### Server side
```Scala
import typedapi.server._

val fetch: String => IO[Result[User]] = name => findUserIO(name).map(success)
val create: User => IO[Result[User]] = user => createUserIO(user).map(success)

val endpoints = deriveAll[IO](MyApi).from(fetch, create)

import typedapi.server.http4s._; import cats.effect.IO; import org.http4s.server.blaze.BlazeBuilder

val sm     = ServerManager(BlazeBuilder[IO], "http://my-host", 8080)
val server = mount(sm, endpoints)

server.unsafeRunSync()
```

This is all you have to do to define an API with multiple endpoints and to create a working client and server for them.

You can find the above code as a complete project [here](https://github.com/pheymann/typedapi/tree/master/docs/example).

## Motivation
This library is the result of the following questions:

> How much can we encode on the type level? Are we able to describe a whole API and generate the call functions from that without using Macros?

It is inspired by [Servant](https://github.com/haskell-servant/servant) and it provides an API layer which is independent of the underlying server/client implementation. Right now Typedapi supports:

  - [http4s](https://github.com/http4s/http4s)
  - [akka-http](https://github.com/akka/akka-http)
  - [scalaj-http](https://github.com/scalaj/scalaj-http) on the client-side
  - ScalaJS on the client-side

If you need something else take a look at this [doc](https://github.com/pheymann/typedapi/blob/master/docs/ExtendIt.md#write-your-own-client-backend).

## Get this library
It is available for Scala 2.11, 2.12 and ScalaJS and can be downloaded as Maven artifact:

```
// dsl
"com.github.pheymann" %% "typedapi-client" % <version>
"com.github.pheymann" %% "typedapi-server" % <version>

// http4s support
"com.github.pheymann" %% "typedapi-http4s-client" % <version>
"com.github.pheymann" %% "typedapi-http4s-server" % <version>

// akka-http support
"com.github.pheymann" %% "typedapi-akka-http-client" % <version>
"com.github.pheymann" %% "typedapi-akka-http-server" % <version>

// Scalaj-Http client support
"com.github.pheymann" %% "typedapi-scalaj-http-client" % <version>

// ScalaJS client support
"com.github.pheymann" %% "typedapi-js-client" % <version>
```

You can also build it on your machine:

```
git clone https://github.com/pheymann/typedapi.git
cd typedapi
sbt "+ publishLocal"
```

## Ammonite
Typedapi also offers an improved experience for [Ammonite](http://ammonite.io/#Ammonite-REPL) and [ScalaScripts](http://ammonite.io/#ScalaScripts):

```Scala
import $ivy.`com.github.pheymann::typedapi-ammonite-client:<version>`

import typedapi._
import client._
import amm._

val Readme = api(Get[MT.`text/html`, String], Root / "pheymann" / "typedapi" / "master" / "README.md")
val readme = derive(Readme)

// gives you the raw scalaj-http response
val cm = clientManager("https://raw.githubusercontent.com")
val response = get().run[Id].raw(cm)

response.body
response.headers
...
```

In case Ammonite cannot resolve `com.dwijnand:sbt-compat:1.0.0`, follow [this](https://github.com/pheymann/typedapi/blob/master/docs/ClientCreation.md#ammonite) solution.

## Documentation
The documentation is located in [docs](https://github.com/pheymann/typedapi/blob/master/docs) and covers the following topics so far:
 - [How to define an API](https://github.com/pheymann/typedapi/blob/master/docs/ApiDefinition.md)
 - [How to create a client](https://github.com/pheymann/typedapi/blob/master/docs/ClientCreation.md)
 - [How to create a server](https://github.com/pheymann/typedapi/blob/master/docs/ServerCreation.md)
 - [Extend the library](https://github.com/pheymann/typedapi/blob/master/docs/ExtendIt.md)
 - Typelevel Summit 2018 Berlin Talk [Typedapi: Define your API on the type-level](https://github.com/pheymann/typelevel-summit-2018)
 - and a [post](https://typelevel.org/blog/2018/06/15/typedapi.html) on the Typelevel Blog describing the basic concept behind this library.

## Dependencies
 - [shapeless 2.3.3](https://github.com/milessabin/shapeless/)

## Contribution
Contributions are highly appreciated. If you find a bug or you are missing the support for a specific client/server library consider opening a PR with your solution.


================================================
FILE: akka-http-client/src/main/scala/typedapi/client/akkahttp/package.scala
================================================
package typedapi.client

import akka.http.scaladsl.HttpExt
import akka.http.scaladsl.model.RequestEntity
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.model.{ HttpMethods, HttpRequest, HttpResponse, Uri }
import akka.http.scaladsl.unmarshalling.{Unmarshal, FromEntityUnmarshaller}
import akka.http.scaladsl.marshalling.{Marshal, ToEntityMarshaller}
import akka.stream.Materializer

import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ ExecutionContext, Future }

package object akkahttp {

  private def mkRequest(uri: String, queries: Map[String, List[String]], headers: Map[String, String]): HttpRequest =
    HttpRequest(
        uri = Uri(uri).withQuery(Uri.Query(queries.mapValues(_.mkString(",")))),
        headers = headers.map { case (key, value) => RawHeader(key, value) }(collection.breakOut)
      )

  private def execRequest(client: HttpExt, request: HttpRequest, bodyConsumerTimeout: FiniteDuration)
                         (implicit ec: ExecutionContext, 
                                   mat: Materializer): Future[HttpResponse] =
    client.singleRequest(request)
      .flatMap { response =>
        response.toStrict(bodyConsumerTimeout)
      }

  def rawGetRequest(bodyConsumerTimeout: FiniteDuration)(implicit ec: ExecutionContext, 
                                                                  mat: Materializer) = new RawGetRequest[HttpExt, Future] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[Resp] = {
      val request = mkRequest(deriveUriString(cm, uri), queries, headers).copy(HttpMethods.GET)

      execRequest(cm.client, request, bodyConsumerTimeout)
    }
  }

  implicit def rawGetRequestImpl(implicit bodyConsumerTimeout: FiniteDuration,
                                          ec: ExecutionContext,
                                          mat: Materializer): RawGetRequest[HttpExt, Future] = rawGetRequest(bodyConsumerTimeout)

  def getRequest[A](bodyConsumerTimeout: FiniteDuration)(implicit decoder: FromEntityUnmarshaller[A],
                                                                  ec: ExecutionContext, 
                                                                  mat: Materializer) = new GetRequest[HttpExt, Future, A] {
    private val raw = rawGetRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def getRequestImpl[A](implicit bodyConsumerTimeout: FiniteDuration,
                                          decoder: FromEntityUnmarshaller[A],
                                          ec: ExecutionContext,
                                          mat: Materializer): GetRequest[HttpExt, Future, A] = getRequest(bodyConsumerTimeout)

  def rawPutRequest(bodyConsumerTimeout: FiniteDuration)(implicit ec: ExecutionContext, 
                                                                  mat: Materializer) = new RawPutRequest[HttpExt, Future] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[Resp] = {
      val request = mkRequest(deriveUriString(cm, uri), queries, headers).copy(HttpMethods.PUT)

      execRequest(cm.client, request, bodyConsumerTimeout)
    }
  }

  implicit def rawPutRequestImpl(implicit bodyConsumerTimeout: FiniteDuration,
                                          ec: ExecutionContext,
                                          mat: Materializer): RawPutRequest[HttpExt, Future] = rawPutRequest(bodyConsumerTimeout)

  def putRequest[A](bodyConsumerTimeout: FiniteDuration)(implicit decoder: FromEntityUnmarshaller[A],
                                                                  ec: ExecutionContext, 
                                                                  mat: Materializer) = new PutRequest[HttpExt, Future, A] {
    private val raw = rawPutRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def putRequestImpl[A](implicit bodyConsumerTimeout: FiniteDuration,
                                          decoder: FromEntityUnmarshaller[A],
                                          ec: ExecutionContext,
                                          mat: Materializer): PutRequest[HttpExt, Future, A] = putRequest(bodyConsumerTimeout)

  def rawPutBodyRequest[Bd](bodyConsumerTimeout: FiniteDuration)(implicit encoder: ToEntityMarshaller[Bd],
                                                                          ec: ExecutionContext, 
                                                                          mat: Materializer) = new RawPutWithBodyRequest[HttpExt, Future, Bd] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[HttpExt]): Future[Resp] = {
      Marshal(body).to[RequestEntity].flatMap { marshalledBody =>
        val request = mkRequest(deriveUriString(cm, uri), queries, headers - "Content-Type").copy(HttpMethods.PUT, entity = marshalledBody)

        execRequest(cm.client, request, bodyConsumerTimeout)
      }
    }
  }

  implicit def rawPutBodyRequestImpl[Bd](implicit bodyConsumerTimeout: FiniteDuration,
                                                  encoder: ToEntityMarshaller[Bd],
                                                  ec: ExecutionContext,
                                                  mat: Materializer): RawPutWithBodyRequest[HttpExt, Future, Bd] = rawPutBodyRequest(bodyConsumerTimeout)

  def putBodyRequest[Bd, A](bodyConsumerTimeout: FiniteDuration)(implicit encoder: ToEntityMarshaller[Bd],
                                                                          decoder: FromEntityUnmarshaller[A],
                                                                          ec: ExecutionContext, 
                                                                          mat: Materializer) = new PutWithBodyRequest[HttpExt, Future, Bd, A] {
    private val raw = rawPutBodyRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, body, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def putBodyRequestImpl[Bd, A](implicit bodyConsumerTimeout: FiniteDuration,
                                                  encoder: ToEntityMarshaller[Bd],
                                                  decoder: FromEntityUnmarshaller[A],
                                                  ec: ExecutionContext,
                                                  mat: Materializer): PutWithBodyRequest[HttpExt, Future, Bd, A] = putBodyRequest(bodyConsumerTimeout)

  def rawPostRequest(bodyConsumerTimeout: FiniteDuration)(implicit ec: ExecutionContext, 
                                                                   mat: Materializer) = new RawPostRequest[HttpExt, Future] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[Resp] = {
      val request = mkRequest(deriveUriString(cm, uri), queries, headers).copy(HttpMethods.POST)

      execRequest(cm.client, request, bodyConsumerTimeout)
    }
  }

  implicit def rawPostRequestImpl(implicit bodyConsumerTimeout: FiniteDuration,
                                           ec: ExecutionContext,
                                           mat: Materializer): RawPostRequest[HttpExt, Future] = rawPostRequest(bodyConsumerTimeout)

  def postRequest[A](bodyConsumerTimeout: FiniteDuration)(implicit decoder: FromEntityUnmarshaller[A],
                                                                   ec: ExecutionContext, 
                                                                   mat: Materializer) = new PostRequest[HttpExt, Future, A] {
    private val raw = rawPostRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def postRequestImpl[A](implicit bodyConsumerTimeout: FiniteDuration,
                                           decoder: FromEntityUnmarshaller[A],
                                           ec: ExecutionContext,
                                           mat: Materializer): PostRequest[HttpExt, Future, A] = postRequest(bodyConsumerTimeout)

  def rawPostBodyRequest[Bd](bodyConsumerTimeout: FiniteDuration)(implicit encoder: ToEntityMarshaller[Bd],
                                                                           ec: ExecutionContext, 
                                                                           mat: Materializer) = new RawPostWithBodyRequest[HttpExt, Future, Bd] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[HttpExt]): Future[Resp] = {
      Marshal(body).to[RequestEntity].flatMap { marshalledBody =>
        val request = mkRequest(deriveUriString(cm, uri), queries, headers - "Content-Type").copy(HttpMethods.POST, entity = marshalledBody)

        execRequest(cm.client, request, bodyConsumerTimeout)
      }
    }
  }

  implicit def rawPostBodyRequestImpl[Bd](implicit bodyConsumerTimeout: FiniteDuration,
                                                   encoder: ToEntityMarshaller[Bd],
                                                   ec: ExecutionContext,
                                                   mat: Materializer): RawPostWithBodyRequest[HttpExt, Future, Bd] = rawPostBodyRequest(bodyConsumerTimeout)

  def postBodyRequest[Bd, A](bodyConsumerTimeout: FiniteDuration)(implicit encoder: ToEntityMarshaller[Bd],
                                                                           decoder: FromEntityUnmarshaller[A],
                                                                           ec: ExecutionContext, 
                                                                           mat: Materializer) = new PostWithBodyRequest[HttpExt, Future, Bd, A] {
    private val raw = rawPostBodyRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, body, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def postBodyRequestImpl[Bd, A](implicit bodyConsumerTimeout: FiniteDuration,
                                                   encoder: ToEntityMarshaller[Bd],
                                                   decoder: FromEntityUnmarshaller[A],
                                                   ec: ExecutionContext,
                                                   mat: Materializer): PostWithBodyRequest[HttpExt, Future, Bd, A] = postBodyRequest(bodyConsumerTimeout)

  def rawDeleteRequest(bodyConsumerTimeout: FiniteDuration)(implicit ec: ExecutionContext, 
                                                                     mat: Materializer) = new RawDeleteRequest[HttpExt, Future] {
    type Resp = HttpResponse

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[Resp] = {
      val request = mkRequest(deriveUriString(cm, uri), queries, headers).copy(HttpMethods.DELETE)

      execRequest(cm.client, request, bodyConsumerTimeout)
    }
  }

  implicit def rawDeleteRequestImpl(implicit bodyConsumerTimeout: FiniteDuration,
                                             ec: ExecutionContext,
                                             mat: Materializer): RawDeleteRequest[HttpExt, Future] = rawDeleteRequest(bodyConsumerTimeout)

  def deleteRequest[A](bodyConsumerTimeout: FiniteDuration)(implicit decoder: FromEntityUnmarshaller[A],
                                                                     ec: ExecutionContext, 
                                                                     mat: Materializer) = new DeleteRequest[HttpExt, Future, A] {
    private val raw = rawDeleteRequest(bodyConsumerTimeout)

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[HttpExt]): Future[A] =
      raw(uri, queries, headers, cm).flatMap { response =>
        Unmarshal(response.entity).to[A]
      }
  }

  implicit def deleteRequestImpl[A](implicit bodyConsumerTimeout: FiniteDuration,
                                             decoder: FromEntityUnmarshaller[A],
                                             ec: ExecutionContext,
                                             mat: Materializer): DeleteRequest[HttpExt, Future, A] = deleteRequest(bodyConsumerTimeout)
}


================================================
FILE: akka-http-server/src/main/scala/typedapi/server/akkahttp/package.scala
================================================
package typedapi.server

import typedapi.shared.MethodType
import shapeless._
import shapeless.ops.hlist.Prepend
import akka.http.scaladsl.{HttpExt, Http}
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling.{Unmarshal, FromEntityUnmarshaller}
import akka.http.scaladsl.marshalling.{Marshal, ToEntityMarshaller}
import akka.stream.Materializer
import akka.stream.scaladsl.Sink

import scala.collection.mutable.Builder
import scala.concurrent.{Future, ExecutionContext}
import scala.annotation.tailrec

package object akkahttp {

  case class AkkaHttpHeaderParseException(msg: String) extends Exception(msg)

  private def getHeaders(raw: Map[String, String]): List[HttpHeader] = 
    raw.map { case (key, value) => HttpHeader.parse(key, value) match {
      case HttpHeader.ParsingResult.Ok(header, _) => header
      case HttpHeader.ParsingResult.Error(cause)  => throw AkkaHttpHeaderParseException(cause.formatPretty)
    }}(collection.breakOut)

  implicit def noReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, FOut](implicit encoder: ToEntityMarshaller[FOut], ec: ExecutionContext) = 
    new NoReqBodyExecutor[El, KIn, VIn, M, Future, FOut] {
      type R   = HttpRequest
      type Out = Future[HttpResponse]

      def apply(req: R, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, VIn, Future, FOut]): Either[ExtractionError, Out] = {
        extract(eReq, endpoint).right.map { extracted =>
          execute(extracted, endpoint).flatMap {
            case Right((code, response)) =>
              Marshal(response).to[ResponseEntity].map { marshalledBody =>
                HttpResponse(status = StatusCode.int2StatusCode(code.statusCode), entity = marshalledBody, headers = getHeaders(endpoint.headers))
              }

            case Left(HttpError(code, msg)) =>
              Future.successful(HttpResponse(status = StatusCode.int2StatusCode(code.statusCode), entity = msg, headers = getHeaders(endpoint.headers)))
          }
        }
      }
    }

  implicit def withReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, Bd, M <: MethodType, ROut <: HList, POut <: HList, FOut]
    (implicit encoder: ToEntityMarshaller[FOut], 
              decoder: FromEntityUnmarshaller[Bd],
              _prepend: Prepend.Aux[ROut, Bd :: HNil, POut], 
              _eqProof: POut =:= VIn,
              mat: Materializer,
              ec: ExecutionContext) = new ReqBodyExecutor[El, KIn, VIn, Bd, M, ROut, POut, Future, FOut] {
    type R   = HttpRequest
    type Out = Future[HttpResponse]

    implicit val prepend = _prepend
    implicit val eqProof = _eqProof

    def apply(req: R, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, (BodyType[Bd], ROut), Future, FOut]): Either[ExtractionError, Out] = {
      extract(eReq, endpoint).right.map { case (_, extracted) =>
        for {
          body     <- Unmarshal(req.entity).to[Bd]
          response <- execute(extracted, body, endpoint).flatMap {
            case Right((code, response)) =>
              Marshal(response).to[ResponseEntity].map { marshalledBody =>
                HttpResponse(status = StatusCode.int2StatusCode(code.statusCode), entity = marshalledBody, headers = getHeaders(endpoint.headers))
              }

            case Left(HttpError(code, msg)) =>
              Future.successful(HttpResponse(status = StatusCode.int2StatusCode(code.statusCode), entity = msg, headers = getHeaders(endpoint.headers)))
          }
        } yield response
      }
    }
  }

  implicit def mountEndpoints(implicit mat: Materializer) = new MountEndpoints[HttpExt, HttpRequest, Future[HttpResponse]] {
    type Out = Future[Http.ServerBinding]

    def apply(server: ServerManager[HttpExt], endpoints: List[Serve[HttpRequest, Future[HttpResponse]]]): Out = {
      val service: HttpRequest => Future[HttpResponse] = request => {
        def execute(eps: List[Serve[HttpRequest, Future[HttpResponse]]], eReq: EndpointRequest): Future[HttpResponse] = eps match {
          case collection.immutable.::(endpoint, tail) => endpoint(request, eReq) match {
            case Right(response)            => response
            case Left(RouteNotFound)        => execute(tail, eReq)
            case Left(BadRouteRequest(msg)) => Future.successful(HttpResponse(400, entity = msg))
          }

          case Nil => Future.successful(HttpResponse(404, entity = "uri = " + request.uri))
        }

        @tailrec
        def toListPath(path: Uri.Path, agg: Builder[String, List[String]]): List[String] = path match {
          case Uri.Path.Slash(tail)      => toListPath(tail, agg)
          case Uri.Path.Segment(p, tail) => toListPath(tail, agg += p)
          case Uri.Path.Empty            => agg.result()
        }

        val eReq = EndpointRequest(
          request.method.name,
          toListPath(request.uri.path, List.newBuilder),
          request.uri.query().toMultiMap,
          request.headers.map(header => header.lowercaseName -> header.value)(collection.breakOut)
        )

        if (request.method.name == "OPTIONS") {
          Future.successful(HttpResponse(headers = getHeaders(optionsHeaders(endpoints, eReq))))
        }
        else
          execute(endpoints, eReq)
      }
    
      server.server.bind(server.host, server.port).to(Sink.foreach { connection =>
        connection.handleWithAsyncHandler(service)
      }).run()
    }
  }
}


================================================
FILE: ammonite-client-support/src/main/scala/typedapi/client/package.scala
================================================
package typedapi.client

import typedapi.util._
import scalaj.http._

package object amm {

  type Id[A]       = scalajhttp.Id[A]
  type Blocking[A] = scalajhttp.Blocking[A]

  def clientManager(host: String, port: Int): ClientManager[Http.type] = ClientManager(Http, host, port)
  def clientManager(host: String): ClientManager[Http.type] = ClientManager(Http, host)

  implicit def rawGetRequestAmm = scalajhttp.rawGetRequest
  implicit def getRequestAmm[A](implicit decoder: Decoder[Id, A]) = scalajhttp.getRequest[A]

  implicit def rawPutRequestAmm = scalajhttp.rawPutRequest
  implicit def putRequestAmm[A](implicit decoder: Decoder[Id, A]) = scalajhttp.putRequest[A]

  implicit def rawPutBodyRequestAmm[Bd](implicit encoder: Encoder[Id, Bd]) = scalajhttp.rawPutBodyRequest[Bd]
  implicit def putBodyRequestAmm[Bd, A](implicit encoder: Encoder[Id, Bd], decoder: Decoder[Id, A]) = scalajhttp.putBodyRequest[Bd, A]

  implicit def rawPostRequestAmm = scalajhttp.rawPostRequest
  implicit def postRequest[A](implicit decoder: Decoder[Id, A]) = scalajhttp.postRequest[A]

  implicit def rawPostBodyRequestAm[Bd](implicit encoder: Encoder[Id, Bd]) = scalajhttp.rawPostBodyRequest[Bd]
  implicit def postBodyRequestAmm[Bd, A](implicit encoder: Encoder[Id, Bd], decoder: Decoder[Id, A]) = scalajhttp.postBodyRequest[Bd, A]

  implicit def rawDeleteRequestAmm = scalajhttp.rawDeleteRequest
  implicit def deleteRequestAmm[A](implicit decoder: Decoder[Id, A]) = scalajhttp.deleteRequest[A]
}


================================================
FILE: build.sbt
================================================
import sbt.Keys._

val `compiler-2.12` = Seq(
  "-deprecation",
  "-encoding", "utf-8",
  "-explaintypes",
  "-feature",
  "-unchecked",
  "-Xfatal-warnings",
  "-Xfuture",
  "-Xlint:inaccessible",
  "-Xlint:infer-any",
  "-Xlint:missing-interpolator",
  "-Xlint:option-implicit",
  "-Xlint:type-parameter-shadow",
  "-Xlint:unsound-match",
  "-Ywarn-dead-code",
  "-Ywarn-inaccessible",
  "-Ywarn-infer-any",
  "-Ywarn-numeric-widen",
  //   "-Ywarn-unused:implicits", -> get errors for implicit evidence
  "-Ywarn-unused:imports",
  //   "-Ywarn-unused:locals",
  "-Ywarn-unused:privates"
)

val `compiler-2.11` = Seq(
  "-deprecation",
  "-encoding", "utf-8",
  "-explaintypes",
  "-feature",
  "-unchecked",
  "-Xfatal-warnings",
  "-Xfuture",
  "-Xlint:inaccessible",
  "-Xlint:infer-any",
  "-Xlint:missing-interpolator",
  "-Xlint:option-implicit",
  "-Xlint:type-parameter-shadow",
  "-Xlint:unsound-match",
  "-Ywarn-dead-code",
  "-Ywarn-numeric-widen",
  "-Ywarn-unused",
  "-Ywarn-inaccessible",
  "-Ywarn-infer-any"
)

lazy val commonSettings = Seq(
  organization  := "com.github.pheymann",
  version       := "0.2.0",
  crossScalaVersions := Seq("2.11.11", "2.12.4"),
  scalaVersion       := "2.12.4",
  scalacOptions      ++= { CrossVersion.partialVersion(scalaVersion.value) match {
    case Some((2, 12)) => `compiler-2.12`
    case Some((2, 11)) => `compiler-2.11`
    case _             => Seq.empty[String]
  }},
  publishTo := sonatypePublishTo.value
)

lazy val mavenSettings = Seq(
  sonatypeProfileName := "pheymann",
  publishMavenStyle   := true,
  pomExtra in Global  := {
    <url>https://github.com/pheymann/typedapi</url>
      <licenses>
        <license>
          <name>MIT</name>
          <url>https://github.com/pheymann/typedapi/blob/master/LICENSE</url>
        </license>
      </licenses>
      <scm>
        <connection>scm:git:github.com/pheymann/typedapi</connection>
        <developerConnection>scm:git:git@github.com:pheymann/typedapi</developerConnection>
        <url>github.com/pheymann/typedapi</url>
      </scm>
      <developers>
        <developer>
          <id>pheymann</id>
          <name>Paul Heymann</name>
          <url>https://github.com/pheymann</url>
        </developer>
      </developers>
  }
)

lazy val typedapi = project
  .in(file("."))
  .settings(commonSettings: _*)
  .aggregate(
    `shared-js`, 
    `shared-jvm`, 
    `client-js`, 
    `client-jvm`, 
    server, 
    `http4s-client`, 
    `http4s-server`, 
    `akka-http-client`,
    `akka-http-server`,
    `js-client`,
    `scalaj-http-client`,
    `http-support-tests`,
    `ammonite-client-support`
  )

lazy val shared = crossProject.crossType(CrossType.Pure)
  .in(file("shared"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-shared",
    libraryDependencies ++= Dependencies.shared
  )

lazy val `shared-js` = shared.js
lazy val `shared-jvm` = shared.jvm

lazy val client = crossProject.crossType(CrossType.Pure)
  .in(file("client"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-client",
    libraryDependencies ++= Dependencies.client
  )
  .dependsOn(shared)

lazy val `client-js` = client.js
lazy val `client-jvm` = client.jvm

lazy val server = project
  .in(file("server"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-server",
    libraryDependencies ++= Dependencies.server
  )
  .dependsOn(`shared-jvm`)


lazy val `http4s-client` = project
  .in(file("http4s-client"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-http4s-client",
    libraryDependencies ++= Dependencies.http4sClient,
  )
  .dependsOn(`client-jvm`)

lazy val `http4s-server` = project
  .in(file("http4s-server"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-http4s-server",
    libraryDependencies ++= Dependencies.http4sServer,
  )
  .dependsOn(server)

lazy val `akka-http-client` = project
  .in(file("akka-http-client"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-akka-http-client",
    libraryDependencies ++= Dependencies.akkaHttpClient
  )
  .dependsOn(`client-jvm`)

lazy val `akka-http-server` = project
  .in(file("akka-http-server"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-akka-http-server",
    libraryDependencies ++= Dependencies.akkaHttpServer
  )
  .dependsOn(server)

lazy val `js-client` = project
  .in(file("js-client"))
  .enablePlugins(ScalaJSPlugin)
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-js-client",
    libraryDependencies ++= Seq(
      "org.scala-js" %%% "scalajs-dom" % "0.9.6" % Compile
    )
  )
  .dependsOn(`client-js`)

lazy val `scalaj-http-client` = project
  .in(file("scalaj-http-client"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-scalaj-http-client",
    libraryDependencies ++= Dependencies.scalajHttpClient
  )
  .dependsOn(`client-jvm`)

lazy val `http-support-tests` = project
  .in(file("http-support-tests"))
  .settings(
    commonSettings,
    parallelExecution in Test := false,
    libraryDependencies ++= Dependencies.httpSupportTests
  )
  .dependsOn(`http4s-client`, `http4s-server`, `akka-http-client`, `akka-http-server`, `scalaj-http-client`)

lazy val `ammonite-client-support` = project
  .in(file("ammonite-client-support"))
  .settings(
    commonSettings,
    mavenSettings,
    name := "typedapi-ammonite-client",
    libraryDependencies ++= Dependencies.ammoniteSupport
  )
  .dependsOn(`scalaj-http-client`)


================================================
FILE: client/src/main/scala/typedapi/client/ApiRequest.scala
================================================
package typedapi.client

import RequestDataBuilder.{Data, DataWithBody}
import typedapi.shared._
import shapeless._

import scala.language.higherKinds
import scala.annotation.implicitNotFound

/** Basic api request element. Provides a function to create an effect representing the actual request. */
@implicitNotFound("""Cannot find RawApiRequest instance for ${M}.

context: ${F}""")
trait RawApiRequest[M <: MethodType, D <: HList, C, F[_]] {

  type Resp

  def apply(data: D, cm: ClientManager[C]): F[Resp]
}

@implicitNotFound("""Cannot find ApiRequest instance for ${M}. Do you miss some implicit value e.g. encoders/decoders?

ouput: ${Out}
context: ${F}""")
trait ApiRequest[M <: MethodType, D <: HList, C, F[_], Out] {

  def apply(data: D, cm: ClientManager[C]): F[Out]
}

trait RawApiWithoutBodyRequest[M <: MethodType, C, F[_]] extends RawApiRequest[M, Data, C, F] {

  def apply(data: Data, cm: ClientManager[C]): F[Resp] = {
    val (uri :: queries :: headers :: HNil): Data = data

    apply(uri, queries, headers, cm)
  }

  def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[C]): F[Resp]
}

trait ApiWithoutBodyRequest[M <: MethodType, C, F[_], Out] extends ApiRequest[M, Data, C, F, Out] {

  def apply(data: Data, cm: ClientManager[C]): F[Out] = {
    val (uri :: queries :: headers :: HNil): Data = data

    apply(uri, queries, headers, cm)
  }

  def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[C]): F[Out]
}

trait RawApiWithBodyRequest[M <: MethodType, C, F[_], Bd] extends RawApiRequest[M, DataWithBody[Bd], C, F] {

  def apply(data: DataWithBody[Bd], cm: ClientManager[C]): F[Resp] = {
    val (uri :: queries :: headers :: body :: HNil): DataWithBody[Bd] = data

    apply(uri, queries, headers, body, cm)
  }

  def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[C]): F[Resp]
}

trait ApiWithBodyRequest[M <: MethodType, C, F[_], Bd, Out] extends ApiRequest[M, DataWithBody[Bd], C, F, Out] {

  def apply(data: DataWithBody[Bd], cm: ClientManager[C]): F[Out] = {
    val (uri :: queries :: headers :: body :: HNil): DataWithBody[Bd] = data

    apply(uri, queries, headers, body, cm)
  }

  def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[C]): F[Out]
}

trait RawGetRequest[C, F[_]] extends RawApiWithoutBodyRequest[GetCall, C, F]
trait GetRequest[C, F[_], A] extends ApiWithoutBodyRequest[GetCall, C, F, A]
trait RawPutRequest[C, F[_]] extends RawApiWithoutBodyRequest[PutCall, C, F]
trait PutRequest[C, F[_], A] extends ApiWithoutBodyRequest[PutCall, C, F, A]
trait RawPutWithBodyRequest[C, F[_], Bd] extends RawApiWithBodyRequest[PutWithBodyCall, C, F, Bd]
trait PutWithBodyRequest[C, F[_], Bd, A] extends ApiWithBodyRequest[PutWithBodyCall, C, F, Bd, A]
trait RawPostRequest[C, F[_]] extends RawApiWithoutBodyRequest[PostCall, C, F]
trait PostRequest[C, F[_], A] extends ApiWithoutBodyRequest[PostCall, C, F, A]
trait RawPostWithBodyRequest[C, F[_], Bd] extends RawApiWithBodyRequest[PostWithBodyCall, C, F, Bd]
trait PostWithBodyRequest[C, F[_], Bd, A] extends ApiWithBodyRequest[PostWithBodyCall, C, F, Bd, A]
trait RawDeleteRequest[C, F[_]] extends RawApiWithoutBodyRequest[DeleteCall, C, F]
trait DeleteRequest[C, F[_], A] extends ApiWithoutBodyRequest[DeleteCall, C, F, A]


================================================
FILE: client/src/main/scala/typedapi/client/ClientManager.scala
================================================
package typedapi.client

/** Provides a supported client instance and some basic configuration. */
final case class ClientManager[C](client: C, host: String, portO: Option[Int]) {

  val base = portO match {
    case Some(p) => s"$host:$p"
    case None    => host
  }
}

object ClientManager {

  def apply[C](client: C, host: String, port: Int): ClientManager[C] = ClientManager(client, host, Some(port))
  def apply[C](client: C, host: String): ClientManager[C] = ClientManager(client, host, None)
}


================================================
FILE: client/src/main/scala/typedapi/client/ExecutableDerivation.scala
================================================
package typedapi.client

import typedapi.shared.{MethodType, MediaType}
import shapeless._
import shapeless.labelled.FieldType

import scala.language.higherKinds

/** Helper class to match the [[RequestDataBuilder]] with an [[ApiRequest]] instance. */
final class ExecutableDerivation[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, MT <: MediaType, O, D <: HList]
  (builder: RequestDataBuilder.Aux[El, KIn, VIn, M, FieldType[MT, O], D], input: VIn) {

  final class Derivation[F[_]] {

    def apply[C](cm: ClientManager[C])(implicit req: ApiRequest[M, D, C, F, O]): F[O] = {
      val data = builder(input, List.newBuilder, Map.empty, Map.empty)

      req(data, cm)
    }

    def raw[C](cm: ClientManager[C])(implicit req: RawApiRequest[M, D, C, F]): F[req.Resp] = {
      val data = builder(input, List.newBuilder, Map.empty, Map.empty)

      req(data, cm)
    }
  }

  def run[F[_]]: Derivation[F] = new Derivation[F]
}


================================================
FILE: client/src/main/scala/typedapi/client/ExecutablesFromHList.scala
================================================
package typedapi.client

import typedapi.shared.{MethodType, MediaType}
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.function.FnFromProduct

/** Derives executables from list of RequestBuilders. */
sealed trait ExecutablesFromHList[H <: HList] {

  type Out <: HList

  def apply(h: H): Out
}

object ExecutablesFromHList extends ExecutablesFromHListLowPrio {

  type Aux[H <: HList, Out0 <: HList] = ExecutablesFromHList[H] { type Out = Out0 }
}

trait ExecutablesFromHListLowPrio {

  implicit val noExecutable = new ExecutablesFromHList[HNil] {
    type Out = HNil

    def apply(h: HNil): Out = HNil
  }

  implicit def deriveExcutable[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, MT <: MediaType, O, D <: HList, T <: HList]
    (implicit next: ExecutablesFromHList[T],
              vinToFn: FnFromProduct[VIn => ExecutableDerivation[El, KIn, VIn, M, MT, O, D]]) =
    new ExecutablesFromHList[RequestDataBuilder.Aux[El, KIn, VIn, M, FieldType[MT, O], D] :: T] {
      type Out = vinToFn.Out :: next.Out

      def apply(comps: RequestDataBuilder.Aux[El, KIn, VIn, M, FieldType[MT, O], D] :: T): Out = {
        val fn = vinToFn.apply(input => new ExecutableDerivation[El, KIn, VIn, M, MT, O, D](comps.head, input))

        fn :: next(comps.tail)
      }
    }
}



================================================
FILE: client/src/main/scala/typedapi/client/FilterServerElements.scala
================================================
package typedapi.client

import typedapi.shared._
import shapeless._

//TODO replace with Typelevelfoldleft
sealed trait FilterServerElements[H <: HList] {

  type Out <: HList
}

sealed trait FilterServerElementsLowPrio {

  implicit val filterServerResult = new FilterServerElements[HNil] {
    type Out = HNil
  }

  implicit def filterServerKeep[El, T <: HList](implicit next: FilterServerElements[T]) = new FilterServerElements[El :: T] {
    type Out = El :: next.Out
  }
}

object FilterServerElements extends FilterServerElementsLowPrio {

  type Aux[H <: HList, Out0 <: HList] = FilterServerElements[H] { type Out = Out0 }

  implicit def filterServerHeaderMatch[K, V, T <: HList](implicit next: FilterServerElements[T]) = new FilterServerElements[ServerHeaderMatchParam[K, V] :: T] {
    type Out = next.Out
  }

  implicit def filterServerHeaderSend[K, V, T <: HList](implicit next: FilterServerElements[T]) = new FilterServerElements[ServerHeaderSendElement[K, V] :: T] {
    type Out = next.Out
  }
}

sealed trait FilterServerElementsList[H <: HList] {

  type Out <: HList
}

object FilterServerElementsList {

  type Aux[H <: HList, Out0 <: HList] = FilterServerElementsList[H] { type Out = Out0 }

  implicit val filterServerListResult = new FilterServerElementsList[HNil] {
    type Out = HNil
  }

  implicit def filterServerListStep[Api <: HList, T <: HList](implicit filtered: FilterServerElements[Api], next: FilterServerElementsList[T]) = 
    new FilterServerElementsList[Api :: T] {
      type Out = filtered.Out :: next.Out
    }
}


================================================
FILE: client/src/main/scala/typedapi/client/RequestDataBuilder.scala
================================================
package typedapi.client

import typedapi.shared._
import shapeless._
import shapeless.labelled.FieldType

import scala.collection.mutable.Builder
import scala.annotation.implicitNotFound

/** Compiles type level api description into a function returning request data (uri, query, header, body) and return-type `A` which are used for a request. */
@implicitNotFound("""Woops, you shouldn't be here. We cannot find the RequestDataBuilder. This seems to be a bug.

elements: ${El}
input keys: ${KIn}
input values: ${VIn}
method: ${M}
expected result: ${O}""")
trait RequestDataBuilder[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O] {

  type Out <: HList

  def apply(inputs: VIn, 
            uri: Builder[String, List[String]], 
            queries: Map[String, List[String]], 
            headers: Map[String, String]): Out
}

object RequestDataBuilder extends RequestDataBuilderMediumPrio {

  type Aux[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O, Out0 <: HList] = RequestDataBuilder[El, KIn, VIn, M, O] { type Out = Out0 }
}

trait RequestDataBuilderLowPrio {

  implicit def pathCompiler[S, T <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O](implicit wit: Witness.Aux[S], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) = 
    new RequestDataBuilder[S :: T, KIn, VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        compiler(inputs, uri += wit.value.toString(), queries, headers)
      }
    }
  
  implicit def segmentInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O](implicit compiler: RequestDataBuilder[T, KIn, VIn, M, O]) = 
    new RequestDataBuilder[SegmentInput :: T, K :: KIn, V :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: V :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val segValue = inputs.head

        compiler(inputs.tail, uri += segValue.toString(), queries, headers)
      }
    }

  implicit def queryInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[QueryInput :: T, K :: KIn, V :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: V :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val queryName  = show.show(wit.value)
        val queryValue = inputs.head

        compiler(inputs.tail, uri, Map((queryName, List(queryValue.toString()))) ++ queries, headers)
      }
    }

  implicit def headerInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[HeaderInput :: T, K :: KIn, V :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: V :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val headerName  = show.show(wit.value)
        val headerValue = inputs.head

        compiler(inputs.tail, uri, queries, Map((headerName, headerValue.toString())) ++ headers)
      }
    }

  implicit def fixedHeaderCompiler[K, V, T <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit kWit: Witness.Aux[K], vWit: Witness.Aux[V], kShow: WitnessToString[K], vShow: WitnessToString[V], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[FixedHeader[K, V] :: T, KIn, VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val key   = kShow.show(kWit.value)
        val value = vShow.show(vWit.value)

        compiler(inputs, uri, queries, Map((key, value)) ++ headers)
      }
    }

  implicit def clientHeaderCompiler[K, V, T <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit kWit: Witness.Aux[K], vWit: Witness.Aux[V], kShow: WitnessToString[K], vShow: WitnessToString[V], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[ClientHeader[K, V] :: T, KIn, VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val key   = kShow.show(kWit.value)
        val value = vShow.show(vWit.value)

        compiler(inputs, uri, queries, Map((key, value)) ++ headers)
      }
    }

  implicit def clientHeaderInputCompiler[K, V, T <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit kWit: Witness.Aux[K], kShow: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[ClientHeaderInput :: T, K :: KIn, V :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: V :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val key   = kShow.show(kWit.value)
        val value = inputs.head.toString

        compiler(inputs.tail, uri, queries, Map((key, value)) ++ headers)
      }
    }

  implicit def clientHeaderCollInputCompiler[V, T <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[ClientHeaderCollInput :: T, KIn, Map[String, V] :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: Map[String, V] :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val coll = inputs.head.mapValues(_.toString)

        compiler(inputs.tail, uri, queries, coll ++ headers)
      }
    }

  type Data             = List[String] :: Map[String, List[String]] :: Map[String, String] :: HNil
  type DataWithBody[Bd] = List[String] :: Map[String, List[String]] :: Map[String, String] :: Bd :: HNil

  private def accept[MT <: MediaType](headers: Map[String, String], media: MT): Map[String, String] =
    Map(("Accept", media.value)) ++ headers

  implicit def getCompiler[MT <: MediaType, A](implicit media: Witness.Aux[MT]) = new RequestDataBuilder[HNil, HNil, HNil, GetCall, FieldType[MT, A]] {
    type Out = Data

    def apply(inputs: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
      val out = uri.result() :: queries :: accept(headers, media.value) :: HNil

      out
    }
  }

  implicit def putCompiler[MT <: MediaType, A](implicit media: Witness.Aux[MT]) = new RequestDataBuilder[HNil, HNil, HNil, PutCall, FieldType[MT, A]] {
    type Out = Data

    def apply(inputs: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
      val out = uri.result() :: queries :: accept(headers, media.value) :: HNil

      out
    }
  }

  implicit def putWithBodyCompiler[BMT <: MediaType, Bd, MT <: MediaType, A](implicit media: Witness.Aux[MT]) = 
    new RequestDataBuilder[HNil, FieldType[BMT, BodyField.T] :: HNil, Bd :: HNil, PutWithBodyCall, FieldType[MT, A]] {
      type Out = DataWithBody[Bd]

      def apply(inputs: Bd :: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val out = uri.result() :: queries :: accept(headers, media.value) :: inputs.head :: HNil

        out
      }
    }

  implicit def postCompiler[MT <: MediaType, A](implicit media: Witness.Aux[MT]) = new RequestDataBuilder[HNil, HNil, HNil, PostCall, FieldType[MT, A]] {
    type Out = Data

    def apply(inputs: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
      val out = uri.result() :: queries :: accept(headers, media.value) :: HNil

      out
    }
  }

  implicit def postWithBodyCompiler[BMT <: MediaType, Bd, MT <: MediaType, A](implicit media: Witness.Aux[MT]) = 
    new RequestDataBuilder[HNil, FieldType[BMT, BodyField.T] :: HNil, Bd :: HNil, PostWithBodyCall, FieldType[MT, A]] {
      type Out = DataWithBody[Bd]

      def apply(inputs: Bd :: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val out = uri.result() :: queries :: accept(headers, media.value) :: inputs.head :: HNil

        out
      }
    }

  implicit def deleteCompiler[MT <: MediaType, A](implicit media: Witness.Aux[MT]) = new RequestDataBuilder[HNil, HNil, HNil, DeleteCall, FieldType[MT, A]] {
    type Out = Data

    def apply(inputs: HNil, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
      val out = uri.result() :: queries :: accept(headers, media.value) :: HNil

      out
    }
  }
}

trait RequestDataBuilderMediumPrio extends RequestDataBuilderLowPrio {

  implicit def queryOptInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[QueryInput :: T, K :: KIn, Option[V] :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: Option[V] :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val queryName      = show.show(wit.value)
        val queryValue     = inputs.head
        val updatedQueries = queryValue.fold(queries)(q => Map(queryName -> List(q.toString())) ++ queries)

        compiler(inputs.tail, uri, updatedQueries, headers)
      }
    }

  implicit def queryListInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[QueryInput :: T, K :: KIn, List[V] :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: List[V] :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val queryName  = show.show(wit.value)
        val queryValue = inputs.head

        if (queryValue.isEmpty)
          compiler(inputs.tail, uri, queries, headers)
        else
          compiler(inputs.tail, uri, Map((queryName, queryValue.map(_.toString()))) ++ queries, headers)
      }
    }

  implicit def headersOptInputCompiler[T <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, O]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], compiler: RequestDataBuilder[T, KIn, VIn, M, O]) =
    new RequestDataBuilder[HeaderInput :: T, K :: KIn, Option[V] :: VIn, M, O] {
      type Out = compiler.Out

      def apply(inputs: Option[V] :: VIn, uri: Builder[String, List[String]], queries: Map[String, List[String]], headers: Map[String, String]): Out = {
        val headerName     = show.show(wit.value)
        val headerValue    = inputs.head
        val updatedHeaders = headerValue.fold(headers)(h => Map(headerName -> h.toString()) ++ headers)

        compiler(inputs.tail, uri, queries, updatedHeaders)
      }
    }
}

@implicitNotFound("""Woops, you shouldn't be here. We cannot find the RequestDataBuilderList. This seems to be a bug.

list: ${H}""")
trait RequestDataBuilderList[H <: HList] {

  type Out <: HList

  def builders: Out
}

object RequestDataBuilderList extends RequestDataBuilderListLowPrio {

  type Aux[H <: HList, Out0 <: HList] = RequestDataBuilderList[H] { type Out = Out0 }
}

trait RequestDataBuilderListLowPrio {

  implicit def lastCompilerList[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O, D <: HList](implicit builder: RequestDataBuilder.Aux[El, KIn, VIn, M, O, D]) = 
    new RequestDataBuilderList[(El, KIn, VIn, M, O) :: HNil] {
      type Out = RequestDataBuilder.Aux[El, KIn, VIn, M, O, D] :: HNil

      val builders = builder :: HNil
    }

  implicit def builderList[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, O, D <: HList, T <: HList](implicit builder: RequestDataBuilder.Aux[El, KIn, VIn, M, O, D], next: RequestDataBuilderList[T]) = 
    new RequestDataBuilderList[(El, KIn, VIn, M, O) :: T] {
      type Out = RequestDataBuilder.Aux[El, KIn, VIn, M, O, D] :: next.Out

      val builders = builder :: next.builders
    }
}


================================================
FILE: client/src/main/scala/typedapi/client/package.scala
================================================
package typedapi

import typedapi.shared._
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.Tupler
import shapeless.ops.function.FnFromProduct

package object client extends TypeLevelFoldLeftLowPrio 
                      with TypeLevelFoldLeftListLowPrio
                      with WitnessToStringLowPrio
                      with ApiTransformer {

  def deriveUriString(cm: ClientManager[_], uri: List[String]): String = cm.base + "/" + uri.mkString("/")

  def derive[H <: HList, FH <: HList, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, MT <: MediaType, Out, D <: HList]
    (apiList: ApiTypeCarrier[H])
    (implicit filter: FilterServerElements.Aux[H, FH],
              folder: Lazy[TypeLevelFoldLeft.Aux[FH, Unit, (El, KIn, VIn, M, FieldType[MT, Out])]],
              builder: RequestDataBuilder.Aux[El, KIn, VIn, M, FieldType[MT, Out], D],
              inToFn: FnFromProduct[VIn => ExecutableDerivation[El, KIn, VIn, M, MT, Out, D]]): inToFn.Out =
    inToFn.apply(input => new ExecutableDerivation[El, KIn, VIn, M, MT, Out, D](builder, input))

  def deriveAll[H <: HList, FH <: HList, In <: HList, Fold <: HList, B <: HList, Ex <: HList]
    (apiLists: CompositionCons[H])
    (implicit filter: FilterServerElementsList.Aux[H, FH],
              folders: TypeLevelFoldLeftList.Aux[FH, Fold],
              builderList: RequestDataBuilderList.Aux[Fold, B],
              executables: ExecutablesFromHList.Aux[B, Ex],
              tupler: Tupler[Ex]): tupler.Out =
    executables(builderList.builders).tupled
}


================================================
FILE: client/src/main/scala/typedapi/client/test/RequestInput.scala
================================================
package typedapi.client.test

final case class ReqInput(method: String,
                          uri: List[String],
                          queries: Map[String, List[String]],
                          headers: Map[String, String])

final case class ReqInputWithBody[Bd](method: String, 
                                      uri: List[String], 
                                      queries: Map[String, List[String]], 
                                      headers: Map[String, String],
                                      body: Bd)


================================================
FILE: client/src/main/scala/typedapi/client/test/package.scala
================================================
package typedapi.client

import scala.language.higherKinds

package object test {

  type TestClientM = ClientManager[Unit]

  val clientManager: TestClientM = ClientManager((), "", 0)

  def testRawGet[F[_]](pure: ReqInput => F[ReqInput]) = new RawGetRequest[Unit, F] {
    type Resp = ReqInput

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[Resp] = 
      pure(ReqInput("GET", uri, queries, headers))
  }
  def testGet[F[_], A](f: ReqInput => F[A]) = new GetRequest[Unit, F, A] {
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[A] = 
      f(ReqInput("GET", uri, queries, headers))
  }

  def testRawPut[F[_]](pure: ReqInput => F[ReqInput]) = new RawPutRequest[Unit, F] {
    type Resp = ReqInput

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[Resp] = 
      pure(ReqInput("PUT", uri, queries, headers))
  }
  def testPut[F[_], A](f: ReqInput => F[A]) = new PutRequest[Unit, F, A] {
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[A] = 
      f(ReqInput("PUT", uri, queries, headers))
  }

  def testRawPutWithBody[F[_], Bd](pure: ReqInputWithBody[Bd] => F[ReqInputWithBody[Bd]]) = 
    new RawPutWithBodyRequest[Unit, F, Bd] {
      type Resp = ReqInputWithBody[Bd]

      def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: TestClientM): F[Resp] = 
        pure(ReqInputWithBody("PUT", uri, queries, headers, body))
    }
  def testPutWithBody[F[_], Bd, A](f: ReqInputWithBody[Bd] => F[A]) = 
    new PutWithBodyRequest[Unit, F, Bd, A] {
      def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: TestClientM): F[A] = 
        f(ReqInputWithBody("PUT", uri, queries, headers, body))
    }

  def testRawPost[F[_]](pure: ReqInput => F[ReqInput]) = new RawPostRequest[Unit, F] {
    type Resp = ReqInput

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[Resp] = 
      pure(ReqInput("POST", uri, queries, headers))
  }
  def testPost[F[_], A](f: ReqInput => F[A]) = new PostRequest[Unit, F, A] {
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[A] = 
      f(ReqInput("POST", uri, queries, headers))
  }

  def testRawPostWithBody[F[_], Bd](pure: ReqInputWithBody[Bd] => F[ReqInputWithBody[Bd]]) = 
    new RawPostWithBodyRequest[Unit, F, Bd] {
    type Resp = ReqInputWithBody[Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: TestClientM): F[Resp] = 
      pure(ReqInputWithBody("POST", uri, queries, headers, body))
  }
  def testPostWithBody[F[_], Bd, A](f: ReqInputWithBody[Bd] => F[A]) = 
    new PostWithBodyRequest[Unit, F, Bd, A] {
      def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: TestClientM): F[A] = 
        f(ReqInputWithBody("POST", uri, queries, headers, body))
    }

  def testRawDelete[F[_]](pure: ReqInput => F[ReqInput]) = new RawDeleteRequest[Unit, F] {
    type Resp = ReqInput

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[Resp] = 
      pure(ReqInput("DELETE", uri, queries, headers))
  }
  def testDelete[F[_], A](f: ReqInput => F[A]) = new DeleteRequest[Unit, F, A] {
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: TestClientM): F[A] = 
      f(ReqInput("DELETE", uri, queries, headers))
  }
}


================================================
FILE: client/src/test/scala/typedapi/client/ClientManagerSpec.scala
================================================
package typedapi.client

import org.specs2.mutable.Specification

final class ClientManagerSpec extends Specification {

  "optional port definition" >> {
    ClientManager((), "my-host", 80).base === "my-host:80"
    ClientManager((), "my-host").base === "my-host"
  }
}


================================================
FILE: client/src/test/scala/typedapi/client/RequestDataBuilderSpec.scala
================================================
package typedapi.client

import typedapi.dsl._
import typedapi.client.test._

import shapeless.Id
import org.specs2.mutable.Specification

final class RequestDataBuilderSpec extends Specification {

  case class Foo()

  type Result = (String, List[String], Map[String, String], Map[String, String], Option[Foo])

  implicit val get       = testGet[Id, ReqInput](identity)
  implicit val put       = testPut[Id, ReqInput](identity)
  implicit def putB[Bd]  = testPutWithBody[Id, Bd, ReqInputWithBody[Bd]](identity)
  implicit val post      = testPost[Id, ReqInput](identity)
  implicit def postB[Bd] = testPostWithBody[Id, Bd, ReqInputWithBody[Bd]](identity)
  implicit val delete    = testDelete[Id, ReqInput](identity)

  "executes compiled api" >> {
    val cm = clientManager

    "single api" >> {
      "method" >> {
        val api0 = derive(:= :> Get[Json, ReqInput])
        api0().run[Id](cm) === ReqInput("GET", Nil, Map(), Map(("Accept", "application/json")))
        val api1 = derive(:= :> Put[Json, ReqInput])
        api1().run[Id](cm) === ReqInput("PUT", Nil, Map(), Map(("Accept", "application/json")))
        val api2 = derive(:= :> Post[Json, ReqInput])
        api2().run[Id](cm) === ReqInput("POST", Nil, Map(), Map(("Accept", "application/json")))
        val api3 = derive(:= :> Delete[Json, ReqInput])
        api3().run[Id](cm) === ReqInput("DELETE", Nil, Map(), Map(("Accept", "application/json")))
      }
      
      "segment" >> {
        val api0 = derive(:= :> Segment[Int]('i0) :> Get[Json, ReqInput])
        api0(0).run[Id](cm) === ReqInput("GET", "0" :: Nil, Map(), Map(("Accept", "application/json")))
        val api1 = derive(:= :> Segment[Int]('i0) :> Segment[Int]('i1) :> Get[Json, ReqInput])
        api1(0, 1).run[Id](cm) === ReqInput("GET", "0" :: "1" :: Nil, Map(), Map(("Accept", "application/json")))
      }

      "query" >> {
        val api0 = derive(:= :> Query[Int]('i0) :> Get[Json, ReqInput])
        api0(0).run[Id](cm) === ReqInput("GET", Nil, Map("i0" -> List("0")), Map(("Accept", "application/json")))
        val api1 = derive(:= :> Query[Int]('i0) :> Query[Int]('i1) :> Get[Json, ReqInput])
        api1(0, 1).run[Id](cm) === ReqInput("GET", Nil, Map("i0" -> List("0"), "i1" -> List("1")), Map(("Accept", "application/json")))
        val api2 = derive(:= :> Query[List[Int]]('i0) :> Get[Json, ReqInput])
        api2(List(0, 1)).run[Id](cm) === ReqInput("GET", Nil, Map("i0" -> List("0", "1")), Map(("Accept", "application/json")))
        api2(Nil).run[Id](cm) === ReqInput("GET", Nil, Map.empty, Map(("Accept", "application/json")))
        val api3 = derive(:= :> Query[Option[Int]]('i0) :> Get[Json, ReqInput])
        api3(Some(0)).run[Id](cm) === ReqInput("GET", Nil, Map("i0" -> List("0")), Map(("Accept", "application/json")))
        api3(None).run[Id](cm) === ReqInput("GET", Nil, Map.empty, Map(("Accept", "application/json")))
      }

      "header" >> {
        val api0 = derive(:= :> Header[Int]('i0) :> Get[Json, ReqInput])
        api0(0).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "0"))
        val api1 = derive(:= :> Header[Int]('i0) :> Header[Int]('i1) :> Get[Json, ReqInput])
        api1(0, 1).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "0", "i1" -> "1"))
        val api2 = derive(:= :> Header[Option[Int]]('i0) :> Get[Json, ReqInput])
        api2(Some(0)).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "0"))
        api2(None).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json"))
        val api3 = derive(:= :> Header('i0, 'i1) :> Get[Json, ReqInput])
        api3().run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "i1"))
        val api4 = derive(:= :> Client.Header[Int]('i0) :> Get[Json, ReqInput])
        api4(0).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "0"))
        val api5 = derive(:= :> Client.Header('i0, 'i1) :> Get[Json, ReqInput])
        api5().run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "i1"))
        val api6 = derive(:= :> Client.Coll[Int] :> Get[Json, ReqInput])
        api6(Map("hello" -> 5)).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "hello" -> "5"))
      }

      "ignore server elements" >> {
        val api0 = derive(:= :> Server.Match[String]("Hello-") :> Server.Send("a", "b") :> Client.Header[Int]('i0) :> Get[Json, ReqInput])
        api0(0).run[Id](cm) === ReqInput("GET", Nil, Map(), Map("Accept" -> "application/json", "i0" -> "0"))
      }

      "request body" >> {
        val api0 = derive(:= :> ReqBody[Json, Int] :> Put[Json, ReqInputWithBody[Int]])
        api0(0).run[Id](cm) === ReqInputWithBody("PUT", Nil, Map(), Map(("Accept", "application/json")), 0)
      }

      "path" >> {
        val api0 = derive(:= :> "hello" :> "world" :> Get[Json, ReqInput])
        api0().run[Id](cm) === ReqInput("GET", "hello" :: "world" :: Nil, Map(), Map(("Accept", "application/json")))
      }
    }

    "raw" >> {
      implicit def rawPutB[Bd] = testRawPutWithBody[Id, Bd](identity)

      val api0 = derive(:= :> ReqBody[Json, Int] :> Put[Json, ReqInputWithBody[Int]])
      api0(0).run[Id].raw(cm) === ReqInputWithBody("PUT", Nil, Map(), Map(("Accept", "application/json")), 0)
    }

    "composition" >> {
      val api = 
        (:= :> "find" :> Server.Match[String]("Hello-") :> Server.Send("a", "b") :> Get[Json, ReqInput]) :|:
        (:= :> "fetch" :> Segment[String]('type) :> Get[Json, ReqInput]) :|:
        (:= :> "store" :> ReqBody[Json, Int] :> Post[Json, ReqInputWithBody[Int]])

      val (find, fetch, store) = deriveAll(api)

      find().run[Id](cm) === ReqInput("GET", "find" :: Nil, Map(), Map(("Accept", "application/json")))
      fetch("all").run[Id](cm) === ReqInput("GET", "fetch" :: "all" :: Nil, Map(), Map(("Accept", "application/json")))
      store(0).run[Id](cm) === ReqInputWithBody("POST", "store" :: Nil, Map(), Map(("Accept", "application/json")), 0)
    }
  }
}


================================================
FILE: docs/ApiDefinition.md
================================================
## How to define an API
The central idea behind Typedapi is to make client and server implementation as boilerplate-free, typesafe and simple as possible.

 - On the client-side you only define what you expect from an API provided by a server. In other words, you define a contract between the client and the server.
 - The server-side then has to comply with that contract by implementing proper endpoint functions.
 
But how do you create this API definitions/contracts? This document will show you two ways provided by Typedapi:
  - use the DSL (`import typedapi.dsl._`)
  - or function-call-like definition (`import typedapi._`)
 
### Base case
Every API has to fullfil the base case, meaning it has to have a root path and a method description:
 
```Scala
// dsl
:= :> Get[MediaTypes.`application/json`, A]

// or
:= :> Get[MT.`application/json`, A]

// or in case of JSON
:= :> Get[Json, A]

// function
api(Get[Json, A])
// or
api(method = Get[Json, A], path = Root)
```
 
This translates to `GET /` returning some `Json A`.

### Methods
So far Typedapi supports the following methods:
 
```Scala
// dsl
:= :> Get[Json, A]
:= :> Put[Json, A]
:= :> Post[Json, A]
:= :> Delete[Json, A]

// function
api(Get[Json, A])
api(Put[Json, A])
api(Post[Json, A])
api(Delete[Json, A])
```
 
### Request Body
You may noticed that `Put` and `Post` don't have a field to describe a request body. To add that you have to explicitly define it with an element in your Api:
 
```Scala
// PUT {body: User} /
// dsl
:= :> ReqBody[Json, B] :> Put[Json, A]

// function
apiWithBody(Put[Json, A], ReqBody[Json, B])

// POST {body: User} /
// dsl
:= :> ReqBody[Json, B] :> Post[Json, A]

// function
apiWithBody(Post[Json, A], ReqBody[Json, B])
```
 
By the way, you can only add `Put` and `Post` as the next element of `ReqBody`. Everything else will not compile. Thus, you end up with a valid API description and not something like `:= :> ReqBody[Json, B] :> Get[Json, A]` or `api(Get[Json, A], ReqBody[Json, B])`.

### One word to encodings
You can find a list of provided encodings [here](https://github.com/pheymann/typedapi/blob/master/shared/src/main/scala/typedapi/shared/MediaTypes.scala). If you need something else implement `trait MediaType`.

### Path
```Scala
// GET /hello/world
// dsl
:= :> "hello" :> "world" :> Get[Json, A]

// function
api(Get[Json, A], Root / "hello" / "world")
```
 
All path elements are translated to singleton types and therefore encoded in the type of the API.
 
### Segment
Have a dynamic path element:
 
```Scala
// GET /user/{name: String}
// dsl
:= :> "user" :> Segment[String]("name") :> Get[Json, A]

// function
api(Get[Json, A], Root / "user" / Segment[String]("name"))
```

Every segment gets a name which is again encoded as singleton type in the API type.

### Query Parameter
```Scala
// GET /query?{id: Int}
// dsl
:= :> "query" :> Query[Int]("id") :> Get[Json, A]

// function
api(Get[Json, A], Root / "query", Queries.add[Int]("id"))
```

Every query gets a name which is again encoded as singleton type in the API type.

#### Optional Query
```Scala
// GET /query/opt?{id: Option[Int]}
// dsl
:= :> "query" :> "opt" :> Query[Option[Int]]("id") :> Get[Json, A]

// function
api(Get[Json, A], Root / "query" / "opt", Queries.add[Option[Int]]("id"))
```

#### Query with a List of elements
```Scala
// GET /query/list?{id: List[Int]}
// dsl
:= :> "query" :> "list" :> Query[List[Int]]("id") :> Get[Json, A]

// function
api(Get[Json, A], Root / "query" / "list", Queries.add[List[Int]]("id"))
```

### Header
```Scala
// GET /header {headers: id: Int}
// dsl
:= :> "header" :> Header[Int]("id") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header", headers = Headers.add[Int]("id"))
```

This header is an expected input parameter.

Every header gets a name which is again encoded as singleton type in the API type.

#### Optional Header
```Scala
// GET /header/opt {headers: id: Option[Int]}
// dsl
:= :> "header" :> "opt" :> Header[Option[Int]]("id") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "opt", headers = Headers.add[Option[Int]]("id"))
```

#### Fixed Headers aka static headers
If you have a set of headers which are statically known and have to be provided by all sides you can add them as follows:

```Scala
// GET /header/fixed {headers: consumer=me}
// dsl
:= :> "header" :> "fixed" :> Header("consumer", "me") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "fixed", headers = Headers.add("consumer", "me"))
```

#### Client-Side: Headers
You have to send headers from the client-side but not server side? Here you go:

```Scala
// GET /header/client {header: consumer: String}
// dsl
:= :> "header" :> "client" :> Client.Header[String]("consumer") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "client", headers = Headers.client[String]("consumer"))
```

#### Client-Side: fixed/static Headers
You have to send static headers from the client-side but not server side? Here you go:

```Scala
// GET /header/client/fixed {header: consumer=me}
// dsl
:= :> "header" :> "client" :> "fixed" :> Client.Header("consumer", "me") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "client" / "fixed", headers = Headers.client("consumer", "me"))
```

#### Client-Side: Header collections
You can send header collections (`Map[String, V]`) as a single argument:

```Scala
// GET /header/client/coll {headers: a:b:...}
//dsl
:= :> "header" :> "client" :> "coll" :> Client.Coll[Int] :> Get[Json, A]

//function
api(Get[Json, A], Root / "header" / "client" / "coll", headers = Headers.clientColl[Int])
```

#### Server-Side: send Headers
You have to send headers from the server-side, e.g. for CORS? Here you go:

```Scala
// GET /header/server/send => {header: consumer=me}
// dsl
:= :> "header" :> "server" :> "send" :> Server.Send("consumer", "me") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "server" / "send", headers = Headers.serverSend("consumer", "me"))
```

#### Server-Side: extract matching Headers keys
You want to extract all headers which contain a certain `String`? Here you go:

```Scala
// GET /header/server/match {header: test1=me, test2=you}
// dsl
:= :> "header" :> "server" :> "match" :> Server.Match[String]("test") :> Get[Json, A]

// function
api(Get[Json, A], Root / "header" / "server" / "match", headers = Headers.serverMatch[String]("test"))
```

This will give you a `Set[V]` with `V = String` in this example.

### Multiple definitions in a single API
You can put multiple definitions into a single API element:

```Scala
val Api =
  (:= :> "hello" :> Get[Json, A]) :|:
  (:= :> "world" :> Query[Int]('foo) :> Delete[B])
```


================================================
FILE: docs/ClientCreation.md
================================================
## Create a client from your API
After we [defined](https://github.com/pheymann/typedapi/blob/master/docs/ApiDefinition.md) our API we have to derive a function/set of functions we can use to make our calls.

```Scala
val Api =
  (api(Get[Json, User], Root / "user" / Segment[String]("name"))) :|:
  (apiWithBody(Put[Json, User], ReqBody[Json, User], Root / "user"))
```

### First thing first, derive your functions
Lets derive our functions:

```Scala
import typedapi.client._

final case class User(name: String)

// implicit encoders and decoders

val (get, create) = deriveAll(Api)
```

### Http4s
If you want to use [http4s](https://github.com/http4s/http4s) as your client backend you have to add the following code:

```Scala
import typedapi.client.http4s._
import org.http4s.client.blaze.Http1Client

val client = Http1Client[IO]().unsafeRunSync
val cm     = ClientManager(client, "http://my-host", myPort)
```

### Akka-Http
If you want to use [akka-http](https://github.com/akka/akka-http) as your client backend you have to add the following code:

```Scala
import typedapi.client.akkahttp._
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http

implicit val timeout = 5.second
implicit val system  = ActorSystem("akka-http-client")
implicit val mat     = ActorMaterializer()

import system.dispatcher

val cm = ClientManager(Http(), "http://my-host", myPort)
```

### Scalaj-Http
If you want to use [scalaj-http](https://github.com/scalaj/scalaj-http) as your client backend you have to add the following code:

```Scala
import typedapi.client.scalajhttp._
import scalaj.http._

val cm = ClientManager(Http, "http://my-host", myPort)
```
Be aware that `typedapi.util` provides an `Encoder[F[_], A]` and `Decoder[F[_], A]` trait to marshall and unmarhsall bodies. You have to provide implementations for your types.

```Scala
implicit val decoder = Decoder[Future, User] { json =>
  // unmarshall the json using some known lib like circe
}

implicit val encoder = Encoder[Future, User] { user =>
  // marshall the user using some known lib like circe
}
```

### Ammonite
There is special support for [Ammonite](http://ammonite.io/#Ammonite-REPL) and [ScalaScripts](http://ammonite.io/#ScalaScripts). It lets you tinker with the raw response and reduces the amount of imports you have to do:

```Scala
import $ivy.`com.github.pheymann::typedapi-ammonite-client:<version>`

import typedapi._
import client._
import amm._

val cm = clientManager("http://localhost", 9000)

final case class User(name: String, age: Int)

val Api = api(Get[Json, User], Root / "user" / "url")

val get = derive(Api)

// gives you the raw scalaj-http response
val response = get().run[Id].raw(cm)

response.body
response.headers
...
```

No `Decoder` needed if you use `raw(cm)`. Under the covers it uses scalaj-http as a client library.

It can be, that Ammonite isn't able to load `com.dwijnand:sbt-compat:1.0.0`. If that is the case execute the following command:

```Scala
interp.repositories() ++= Seq(coursier.ivy.IvyRepository.fromPattern(
  "https://dl.bintray.com/dwijnand/sbt-plugins/" +:
  coursier.ivy.Pattern.default
))
```

### ScalaJS
If you want to compile to [ScalaJS](https://www.scala-js.org/) you have to use the [Ajax](https://github.com/scala-js/scala-js-dom/blob/master/src/main/scala/org/scalajs/dom/ext/Extensions.scala#L253) with:

```Scala
import typedapi.client.js._
import org.scalajs.dom.ext.Ajax

val cm = ClientManager(Ajax, "http://my-host", myPort)
```

Be aware that `typedapi.util` provides an `Encoder[F[_], A]` and `Decoder[F[_], A]` trait to marshall and unmarhsall bodies. You have to provide implementations for your types.

```Scala
implicit val decoder = Decoder[Future, User] { json =>
  // unmarshall the json using some known lib like circe
}

implicit val encoder = Encoder[Future, User] { user =>
  // marshall the user using some known lib like circe
}
```

### Usage
Now we can to use our client functions:

```Scala
for {
  _    <- create(User("Joe", 42)).run[IO](cm)
  user <- get("Joe").run[IO](cm)
} yield user

//F[User]
```

**Make sure** you have the proper encoders and decoders in place.


================================================
FILE: docs/ExtendIt.md
================================================
## Extend Typedapi to fit your needs
You ended up in this file if:
 - the default implements for a HTTP framework doesn't fit your needs
 - if the framework you want to use is not supported
 - you need more specilized [RequestDataBuilder](https://github.com/pheymann/typedapi/blob/master/client/src/main/scala/typedapi/client/RequestDataBuilder.scala), [RouteExtractors](https://github.com/pheymann/typedapi/blob/master/server/src/main/scala/typedapi/server/RouteExtractor.scala), [MediaTypes](https://github.com/pheymann/typedapi/blob/master/shared/src/main/scala/typedapi/shared/ApiElement.scala#L58), or the like

### General remark
I kept most of the type-classes open. That means you can override them as you like. If a certain class like *RequestDataBuilder* doesn't fullfil your needs just add another instance. Take a look at available implementations to get an idea how it works or ask a question in Gitter.

### Write your own Client backend
To write your own client backend you have to implement the [ApiRequest](https://github.com/pheymann/typedapi/blob/master/client/src/main/scala/typedapi/client/ApiRequest.scala) type-classes:
  - `GetRequest`
  - `PutRequest` and `PutWithBodyRequest`
  - `PostRequest` and `PostWithBodyRequest`
  - `DeleteRequest`

Take a look into [http4s-client](https://github.com/pheymann/typedapi/blob/master/http4s-client/src/main/scala/typedapi/client/http4s/package.scala) to get an idea how to do it.

You can implement all type-classes or just a subset to override implementations provided by TypedApi.

### Write your own Server backend
To write your own server backend you have to implement the [EndpointExecutor](https://github.com/pheymann/typedapi/blob/master/server/src/main/scala/typedapi/server/EndpointExecutor.scala) and [MountEndpoints](https://github.com/pheymann/typedapi/blob/master/server/src/main/scala/typedapi/server/ServerManager.scala) type-classes

Take a look into [http4s-server](https://github.com/pheymann/typedapi/blob/master/http4s-server/src/main/scala/typedapi/server/http4s/package.scala) to get an idea how to do it.

You can implement all type-classes or just a subset to override implementations provided by TypedApi.


================================================
FILE: docs/ServerCreation.md
================================================
## Create a server from your API
After we [defined](https://github.com/pheymann/typedapi/blob/master/docs/ApiDefinition.md) our API we have to derive the endpoint/set of endpoints we can mount and serve to the world.

```Scala
val Api =
  (api(Get[Json, User], Root / "user" / Segment[String]("name"))) :|:
  (apiWithBody(Put[Json, User], ReqBody[Json, User], Root / "user"))
```

### First things first, derive the endpoints
```Scala
import typedapi.server._

final case class User(name: String)

// implicit encoders and decoders

val endpoints = deriveAll[IO](Api).from(
  name => // retrieve and return user
  user => // store user
)
```

### Set Status Codes
#### Success
```Scala
deriveAll[IO](Api).from(
  name =>
      val user: User = ???
      
      success(user) // creates a 200
  ...
}
```

or

```Scala
deriveAll[IO](Api).from(
  name =>
      val user: User = ???
      
      successWith(StatusCodes.Ok)(user) // set code
  ...
}
```

#### Error
```Scala
deriveAll[IO](Api).from(
  name =>
      val user: Option[User] = ???
      
      user.fold(errorWith(StatusCodes.NotFound, s"no user $id")(user => success(user))
  ...
}
```

### Http4s
If you want to use [http4s](https://github.com/http4s/http4s) as your server backend you have to add the following code:

```Scala
import typedapi.server.http4s._
import org.http4s.server.blaze.BlazeBuilder

val sm = ServerManager(BlazeBuilder[IO], "http://my-host", myPort)
```

### Akka-Http
If you want to use [akka-http](https://github.com/akka/akka-http) as your server backend you have to add the following code:

```Scala
implicit val timeout = 5.second
implicit val system  = ActorSystem("akka-http-server")
implicit val mat     = ActorMaterializer()

import system.dispatcher
    
val sm = ServerManager(Http(), "http://my-host", myPort)
```

### Start server
Now we can mount `endpoints` and serve to to the world:

```Scala
val server = mount(sm, endpoints)

server.unsafeRunSync()
```

**Make sure** you have the proper encoders and decoders in place.


================================================
FILE: docs/example/ammonite_client_example.sc
================================================
import $ivy.`com.github.pheymann::typedapi-ammonite-client:0.2.0-M1`

import typedapi._
import client._
import amm._

val cm = clientManager("http://localhost", 9000)

final case class User(name: String, age: Int)

val Api = api(Get[Json, User], Root, headers = Headers.serverSend("Access-Control-Allow-Origin", "*"))

val get = derive(Api)

val response = get().run[Id].raw(cm)


================================================
FILE: docs/example/build.sbt
================================================

val typedapiVersion = "0.2.0"
val http4sVersion   = "0.18.0"

val commonSettings = Seq(
  scalaVersion := "2.12.4"
)

lazy val root = project
  .in(file("."))
  .aggregate(`shared-jvm`, `shared-js`, `client-jvm`, `client-js`, server)

lazy val shared = crossProject.crossType(CrossType.Pure)
  .in(file("shared"))
  .settings(commonSettings: _*)
  .settings(
    libraryDependencies ++= Seq(
      "com.github.pheymann" %%% "typedapi-shared" % typedapiVersion,

      "io.circe" %%% "circe-core" % "0.9.1",
      "io.circe" %%% "circe-parser" % "0.9.1",
      "io.circe" %%% "circe-generic" % "0.9.1"
    )
  )

lazy val `shared-js` = shared.js
lazy val `shared-jvm` = shared.jvm

lazy val server = project
  .in(file("server"))
  .settings(commonSettings: _*)
  .settings(
    libraryDependencies ++= Seq(
      "com.github.pheymann" %% "typedapi-http4s-server" % typedapiVersion,

      "org.http4s" %% "http4s-circe" % http4sVersion,
      "org.http4s" %% "http4s-blaze-server" % http4sVersion,
      "org.http4s" %% "http4s-dsl" % http4sVersion
    )
  )
  .dependsOn(`shared-jvm`)

lazy val `client-jvm` = project
  .in(file("client-jvm"))
  .settings(commonSettings: _*)
  .settings(
    libraryDependencies ++= Seq(
      "com.github.pheymann" %% "typedapi-http4s-client" % typedapiVersion,

      "org.http4s" %% "http4s-circe" % http4sVersion,
      "org.http4s" %% "http4s-blaze-client" % http4sVersion,
      "org.http4s" %% "http4s-dsl" % http4sVersion
    )
  )
  .dependsOn(`shared-jvm`)

lazy val `client-js` = project
  .in(file("client-js"))
  .enablePlugins(ScalaJSPlugin)
  .settings(commonSettings: _*)
  .settings(
    libraryDependencies ++= Seq(
      "com.github.pheymann" %%% "typedapi-js-client" % typedapiVersion,
    ),
    scalaJSUseMainModuleInitializer := true
  )
  .dependsOn(`shared-js`)


================================================
FILE: docs/example/client-js/index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <body>
    <script src="target/scala-2.12/client-js-fastopt.js"></script>
  </body>
</html>


================================================
FILE: docs/example/client-js/src/main/scala/Client.scala
================================================

import typedapi.client._
import typedapi.client.js._
import org.scalajs.dom.ext.Ajax
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Client {

  type Id[A] = A

  final case class DecodeException(msg: String) extends Exception

  implicit val decoder = typedapi.util.Decoder[Future, User](json => decode[User](json).fold(
    error => Future.successful(Left(DecodeException(error.toString()))), 
    user  => Future.successful(Right(user))
  ))
  implicit val encoder = typedapi.util.Encoder[Future, User](user => Future.successful(user.asJson.noSpaces))

  val (get, put, post, delete, path, putBody, segment, search, header, fixed, client, coll, matches) = deriveAll(FromDsl.MyApi)

  def main(args: Array[String]): Unit = {
    val cm = ClientManager(Ajax, "http://localhost", 9000)

    (for {
      u0 <- putBody(User("joe", 27)).run[Future](cm)
      u1 <- search("joe").run[Future](cm)
    } yield (u0, u1)).foreach { case (u0, u1) =>
      println(u0)
      println(u1)
    }
  }
}


================================================
FILE: docs/example/client-jvm/src/main/scala/Client.scala
================================================

import typedapi.client._
import typedapi.client.http4s._
import cats.effect.IO
import org.http4s._
import org.http4s.circe._

object Client {

  implicit val decoder = jsonOf[IO, User]
  implicit val encoder = jsonEncoderOf[IO, User]

  val (get, put, post, delete, path, putBody, segment, search, header, fixed, client, coll, matches) = deriveAll(FromDsl.MyApi)

  def main(args: Array[String]): Unit = {
    import User._
    import cats.effect.IO
    import org.http4s.client.blaze.Http1Client

    val cm = ClientManager(Http1Client[IO]().unsafeRunSync, "http://localhost", 9000)

    (for {
      u0 <- putBody(User("joe", 27)).run[IO](cm)
      u1 <- search("joe").run[IO](cm)
    } yield {
      println(u0)
      println(u1)
      ()
    }).unsafeRunSync()
  }
}


================================================
FILE: docs/example/project/build.properties
================================================
sbt.version=1.0.4

================================================
FILE: docs/example/project/plugins.sbt
================================================
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22")


================================================
FILE: docs/example/server/src/main/scala/Server.scala
================================================

import typedapi.server._
import typedapi.server.http4s._
import cats.effect.IO
import org.http4s._
import org.http4s.circe._

object Server {

  implicit val decoder = jsonOf[IO, User]
  implicit val encoder = jsonEncoderOf[IO, User]

  val get: () => IO[Result[User]] = () => IO.pure(success(User("Joe", 42)))
  val put: () => IO[Result[User]] = get
  val post: () => IO[Result[User]] = get
  val delete: () => IO[Result[User]] = get

  val path: () => IO[Result[User]] = get

  val putBody: User => IO[Result[User]] = user => IO.pure(success(user))
  val segment: String => IO[Result[User]] = name => IO.pure(success(User(name, 42)))
  val search: String => IO[Result[User]] = segment

  val header: String => IO[Result[User]] = consumer => IO.pure(success(User("found: " + consumer, 42)))
  val fixed: () => IO[Result[User]] = get
  val client: () => IO[Result[User]] = get
  val coll: () => IO[Result[User]] = get
  val matching: Map[String, String] => IO[Result[User]] = matches => IO.pure(success(User(matches.mkString(","), 42)))

  val endpoints = deriveAll[IO](FromDefinition.MyApi).from(
    get, 
    put, 
    post, 
    delete, 
    path, 
    putBody, 
    segment, 
    search, 
    header, 
    fixed, 
    client, 
    coll, 
    matching
  )

  def main(args: Array[String]): Unit = {
    import org.http4s.server.blaze.BlazeBuilder

    val sm = ServerManager(BlazeBuilder[IO], "localhost", 9000)

    mount(sm, endpoints).unsafeRunSync()

    scala.io.StdIn.readLine("Press 'Enter' to stop ...")
  }
}


================================================
FILE: docs/example/shared/src/main/scala/Apis.scala
================================================

object FromDsl {

  import typedapi.dsl._

  /* NOTE: we have to add the 'Access-Control-Allow-Origin' header to the server-side to allow the 
   * browser (ScalaJS) to access the server (CORS)
   */

  val MyApi = 
    // basic GET request
    (:= :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    // basic PUT request
    (:= :> Server.Send("Access-Control-Allow-Origin", "*") :> Put[Json, User]) :|:
    // basic POST request
    (:= :> Server.Send("Access-Control-Allow-Origin", "*") :> Post[Json, User]) :|:
    // basic DELETE request
    (:= :> Server.Send("Access-Control-Allow-Origin", "*") :> Delete[Json, User]) :|:
    // define a path
    (:= :> "my" :> "path" :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    // add a request body
    (:= :> "with" :> "body" :> Server.Send("Access-Control-Allow-Origin", "*") :> ReqBody[Json, User] :> Put[Json, User]) :|:
    // add segments
    (:= :> "name" :> Segment[String]("name") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    // add query
    (:= :> "search" :> "user" :> Query[String]("name") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    // add header
    (:= :> "header" :> Header[String]("consumer") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "fixed" :> Header("consumer", "me") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "client" :> Client.Header("client", "me") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "client" :> "coll" :> Client.Coll[Int] :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "server" :> Server.Match[String]("Control-") :> Server.Send("Access-Control-Allow-Origin", "*") :> Get[Json, User])
}

object FromDefinition {

  import typedapi._

  val MyApi =
    // basic GET request
    api(Get[Json, User], headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // basic PUT request
    api(Put[Json, User], headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // basic POST request
    api(Post[Json, User], headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // basic Delete request
    api(Delete[Json, User], headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // define a path
    api(Get[Json, User], Root / "my" / "path", headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // add a request body
    apiWithBody(Put[Json, User], ReqBody[Json, User], Root / "with" / "body", headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // add segments
    api(Get[Json, User], Root / "name" / Segment[String]("name"), headers = Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // add query
    api(Get[Json, User], Root / "search" / "user", Queries.add[String]("name"), Headers.serverSend("Access-Control-Allow-Origin", "*")) :|:
    // add header
    api(Get[Json, User], Root / "header", headers = Headers.add[String]("consumer").serverSend("Access-Control-Allow-Origin", "*")) :|:
    api(Get[Json, User], Root / "header" / "fixed", headers = Headers.add("consumer", "me").serverSend("Access-Control-Allow-Origin", "*")) :|:
    api(Get[Json, User], Root / "header" / "client", headers = Headers.client("client", "me").serverSend("Access-Control-Allow-Origin", "*")) :|:
    api(Get[Json, User], Root / "header" / "client" / "coll", headers = Headers.clientColl[Int].serverSend("Access-Control-Allow-Origin", "*")) :|:
    api(Get[Json, User], Root / "header" / "server", headers = Headers.serverMatch[String]("Control-").serverSend("Access-Control-Allow-Origin", "*"))
}


================================================
FILE: docs/example/shared/src/main/scala/User.scala
================================================

import io.circe.syntax._
import io.circe.generic.JsonCodec

final case class User(name: String, age: Int)

object User {

  implicit val enc = io.circe.generic.semiauto.deriveEncoder[User]
  implicit val dec = io.circe.generic.semiauto.deriveDecoder[User]
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/User.scala
================================================
package http.support.tests

import cats.effect.IO
import io.circe.generic.semiauto._
import org.http4s.circe._
import org.http4s.dsl.io._

final case class User(name: String, age: Int)

object UserCoding {

  implicit val enc = deriveEncoder[User]
  implicit val dec = deriveDecoder[User]

  implicit val decoderIO = jsonOf[IO, User]
  implicit val encoderIO = jsonEncoderOf[IO, User]
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/client/AkkaHttpClientSupportSpec.scala
================================================
package http.support.tests.client

import http.support.tests.{User, UserCoding, Api}
import typedapi.client._
import typedapi.client.akkahttp._
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import org.specs2.mutable.Specification
import org.specs2.concurrent.ExecutionEnv

import scala.concurrent.duration._
import scala.concurrent.{Future, Await}

final class AkkaHttpClientSupportSpec(implicit ee: ExecutionEnv) extends Specification {

  import UserCoding._
  import FailFastCirceSupport._

  sequential

  implicit val timeout = 5.second
  implicit val system  = ActorSystem("akka-http-client-spec", defaultExecutionContext = Some(ee.ec))
  implicit val mat     = ActorMaterializer()

  import system.dispatcher

  val cm     = ClientManager(Http(), "http://localhost", 9001)
  val server = TestServer.start()

  "akka http client support" >> {
    val (p, s, q, header, fixed, clInH, clFixH, clColl, serMatchH, serSendH, m0, m1, m2, m3, m4, m5, _, _, _) = deriveAll(Api)

    "paths and segments" >> {
      p().run[Future](cm) must beEqualTo(User("foo", 27)).awaitFor(timeout)
      s("jim").run[Future](cm) must beEqualTo(User("jim", 27)).awaitFor(timeout)
    }
    
    "queries" >> {
      q(42).run[Future](cm) must beEqualTo(User("foo", 42)).awaitFor(timeout)
    }
    
    "headers" >> {
      header(42).run[Future](cm) must beEqualTo(User("foo", 42)).awaitFor(timeout)
      fixed().run[Future](cm) must beEqualTo(User("joe", 27)).awaitFor(timeout)
      clInH("jim").run[Future](cm) must beEqualTo(User("jim", 27)).awaitFor(timeout)
      clFixH().run[Future](cm) must beEqualTo(User("joe", 27)).awaitFor(timeout)
      clColl(Map("coll" -> "joe", "collect" -> "jim")).run[Future](cm) must beEqualTo(User("coll: joe,collect: jim", 27)).awaitFor(timeout)
      serMatchH().run[Future](cm) must beEqualTo(User("joe", 27)).awaitFor(timeout)
      serSendH().run[Future](cm) must beEqualTo(User("joe", 27)).awaitFor(timeout)
    }

    "methods" >> {
      m0().run[Future](cm) must beEqualTo(User("foo", 27)).awaitFor(timeout)
      m1().run[Future](cm) must beEqualTo(User("foo", 27)).awaitFor(timeout)
      m2(User("jim", 42)).run[Future](cm) must beEqualTo(User("jim", 42)).awaitFor(timeout)
      m3().run[Future](cm) must beEqualTo(User("foo", 27)).awaitFor(timeout)
      m4(User("jim", 42)).run[Future](cm) must beEqualTo(User("jim", 42)).awaitFor(timeout)
      m5(List("because")).run[Future](cm) must beEqualTo(User("foo", 27)).awaitFor(timeout)
    }

    step {
      server.shutdown.unsafeRunSync()
      Await.ready(system.terminate, timeout)
    }
  }
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/client/Http4sClientSupportSpec.scala
================================================
package http.support.tests.client

import http.support.tests.{UserCoding, User, Api}
import typedapi.client._
import typedapi.client.http4s._
import cats.effect.IO
import org.http4s.client.blaze.Http1Client
import org.specs2.mutable.Specification

final class Http4sClientSupportSpec extends Specification {

  import UserCoding._

  sequential

  val cm = ClientManager(Http1Client[IO]().unsafeRunSync, "http://localhost", 9001)

  val server = TestServer.start()

  "http4s client support" >> {
    val (p, s, q, header, fixed, clInH, clFixH, clColl, serMatchH, serSendH, m0, m1, m2, m3, m4, m5, _, _, _) = deriveAll(Api)

    "paths and segments" >> {
      p().run[IO](cm).unsafeRunSync() === User("foo", 27)
      s("jim").run[IO](cm).unsafeRunSync() === User("jim", 27)
    }
    
    "queries" >> {
      q(42).run[IO](cm).unsafeRunSync() === User("foo", 42)
    }
    
    "headers" >> {
      header(42).run[IO](cm).unsafeRunSync() === User("foo", 42)
      fixed().run[IO](cm).unsafeRunSync() === User("joe", 27)
      clInH("jim").run[IO](cm).unsafeRunSync === User("jim", 27)
      clFixH().run[IO](cm).unsafeRunSync() === User("joe", 27)
      clColl(Map("coll" -> "joe", "collect" -> "jim")).run[IO](cm).unsafeRunSync === User("coll: joe,collect: jim", 27)
      serMatchH().run[IO](cm).unsafeRunSync() === User("joe", 27)
      serSendH().run[IO](cm).unsafeRunSync() === User("joe", 27)
    }

    "methods" >> {
      m0().run[IO](cm).unsafeRunSync() === User("foo", 27)
      m1().run[IO](cm).unsafeRunSync() === User("foo", 27)
      m2(User("jim", 42)).run[IO](cm).unsafeRunSync() === User("jim", 42)
      m3().run[IO](cm).unsafeRunSync() === User("foo", 27)
      m4(User("jim", 42)).run[IO](cm).unsafeRunSync() === User("jim", 42)
      m5(List("because")).run[IO](cm).unsafeRunSync() === User("foo", 27)
    }

    step {
      server.shutdown.unsafeRunSync()
    }
  }
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/client/ScalajHttpClientSupportSpec.scala
================================================
package http.support.tests.client

import http.support.tests.{UserCoding, User, Api}
import typedapi.client._
import typedapi.client.scalajhttp._
import scalaj.http.Http
import io.circe.parser._
import io.circe.syntax._
import org.specs2.mutable.Specification

final class ScalajHttpClientSupportSpec extends Specification {

  import UserCoding._

  sequential

  case class DecodeException(msg: String) extends Exception

  implicit val decoder = typedapi.util.Decoder[Id, User](json => decode[User](json).fold(
    error => Left(DecodeException(error.toString())),
    user  => Right(user)
  ))
  implicit val encoder = typedapi.util.Encoder[Id, User](user => user.asJson.noSpaces)

  val cm = ClientManager(Http, "http://localhost", 9001)

  val server = TestServer.start()

  "http4s client support" >> {
    val (p, s, q, header, fixed, clInH, clFixH, clColl, serMatchH, serSendH, m0, m1, m2, m3, m4, m5, _, _, _) = deriveAll(Api)

    "paths and segments" >> {
      p().run[Blocking](cm) === Right(User("foo", 27))
      s("jim").run[Blocking](cm) === Right(User("jim", 27))
    }
    
    "queries" >> {
      q(42).run[Blocking](cm) === Right(User("foo", 42))
    }
    
    "headers" >> {
      header(42).run[Blocking](cm) === Right(User("foo", 42))
      fixed().run[Blocking](cm) === Right(User("joe", 27))
      clInH("jim").run[Blocking](cm) === Right(User("jim", 27))
      clFixH().run[Blocking](cm) === Right(User("joe", 27))
      clColl(Map("coll" -> "joe", "collect" -> "jim")).run[Blocking](cm) === Right(User("collect: jim,coll: joe", 27))
      serMatchH().run[Blocking](cm) === Right(User("joe", 27))
      serSendH().run[Blocking](cm) === Right(User("joe", 27))
    }

    "methods" >> {
      m0().run[Blocking](cm) === Right(User("foo", 27))
      m1().run[Blocking](cm) === Right(User("foo", 27))
      m2(User("jim", 42)).run[Blocking](cm) === Right(User("jim", 42))
      m3().run[Blocking](cm) === Right(User("foo", 27))
      m4(User("jim", 42)).run[Blocking](cm) === Right(User("jim", 42))
      m5(List("because")).run[Blocking](cm) === Right(User("foo", 27))
    }

    "raw" >> {
      m0().run[Id].raw(cm).body === """{"name":"foo","age":27}"""
    }

    step {
      server.shutdown.unsafeRunSync()
    }
  }
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/client/TestServer.scala
================================================
package http.support.tests.client

import http.support.tests.{User, UserCoding}
import cats.effect.IO
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.io._
import org.http4s.server.Server
import org.http4s.server.blaze._

object Age extends QueryParamDecoderMatcher[Int]("age")

object Reasons {
  def unapplySeq(params: Map[String, Seq[String]]) = params.get("reasons")
  def unapply(params: Map[String, Seq[String]]) = unapplySeq(params)
}

object TestServer {

  import UserCoding._
   
  val service = HttpService[IO] {
    case GET -> Root / "path" => Ok(User("foo", 27))
    case GET -> Root / "segment" / name => Ok(User(name, 27))

    case GET -> Root / "query" :? Age(age) => Ok(User("foo", age))

    case req @ GET -> Root / "header" => 
      val headers = req.headers.toList
      val age     = headers.find(_.name.value == "age").get.value.toInt

      Ok(User("foo", age))

    case req @ GET -> Root / "header" / "fixed" => 
      val headers = req.headers.toList
      val value   = headers.find(_.name.value == "Hello").get.value

      if (value != "*")
        throw new IllegalArgumentException("unexpected header value " + value)

      Ok(User("joe", 27))

    case req @ GET -> Root / "header" / "client" =>
      val headers = req.headers.toList
      val value   = headers.find(_.name.value == "Hello").get.value

      if (value != "*")
        throw new IllegalArgumentException("unexpected header value " + value)

      Ok(User("joe", 27))

    case req @ GET -> Root / "header" / "client" / "coll" =>
      val headers = req.headers.toList
      val values  = headers.filter(_.name.value.contains("coll"))

      if (values.isEmpty)
        throw new IllegalArgumentException("no header collection ")

      Ok(User(values.mkString(","), 27))

    case req @ GET -> Root / "header" / "input" / "client" =>
      val headers = req.headers.toList
      val value   = headers.find(_.name.value == "Hello").get.value

      Ok(User(value, 27))

    case req @ GET -> Root / "header" / "server" / "send" =>
      Ok(User("joe", 27)).map(resp => resp.copy(headers = resp.headers put Header("Hello", "*")))

    case req @ GET -> Root / "header" / "server" / "match" =>
      Ok(User("joe", 27))

    case GET -> Root => Ok(User("foo", 27))
    case PUT -> Root => Ok(User("foo", 27))
    case req @ PUT -> Root / "body" => Ok(User("foo", 27))
      for {
        user <- req.as[User]
        resp <- Ok(user.asJson)
      } yield resp

    case POST -> Root => Ok(User("foo", 27))
    case req @ POST -> Root / "body" => Ok(User("foo", 27))
      for {
        user <- req.as[User]
        resp <- Ok(user.asJson)
      } yield resp

    case DELETE -> Root :? Reasons(reasons) => 
      println(reasons)
      Ok(User("foo", 27))
  }

  def start(): Server[IO] = {
    val builder = BlazeBuilder[IO]
      .bindHttp(9001, "localhost")
      .mountService(service, "/")
      .start

    builder.unsafeRunSync()
  }
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/package.scala
================================================
package http.support

import typedapi.dsl._

package object tests {

  val Api =
    (:= :> "path" :> Get[Json, User]) :|:
    (:= :> "segment" :> Segment[String]('name) :> Get[Json, User]) :|:
    (:= :> "query" :> Query[Int]('age) :> Get[Json, User]) :|:
    (:= :> "header" :> Header[Int]('age) :> Get[Json, User]) :|:
    (:= :> "header" :> "fixed" :> Header("Hello", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "input" :> "client" :> Client.Header[String]("Hello") :> Get[Json, User]) :|:
    (:= :> "header" :> "client" :> Client.Header("Hello", "*") :> Get[Json, User]) :|:
    (:= :> "header" :> "client" :> "coll" :> Client.Coll[String] :> Get[Json, User]) :|:
    (:= :> "header" :> "server" :> "match" :> Server.Match[String]("test") :> Get[Json, User]) :|:
    (:= :> "header" :> "server" :> "send" :> Server.Send("Hello", "*") :> Get[Json, User]) :|:
    (:= :> Get[Json, User]) :|:
    (:= :> Put[Json, User]) :|:
    (:= :> "body" :> ReqBody[Json, User] :> Put[Json, User]) :|:
    (:= :> Post[Json, User]) :|:
    (:= :> "body" :> ReqBody[Json, User] :> Post[Json, User]) :|:
    (:= :> Query[List[String]]('reasons) :> Delete[Json, User]) :|:
    (:= :> "status" :> "200" :> Get[Plain, String]) :|:
    (:= :> "status" :> "400" :> Get[Plain, String]) :|:
    (:= :> "status" :> "500" :> Get[Plain, String])
}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/server/AkkaHttpServerSupportSpec.scala
================================================
package http.support.tests.server

import http.support.tests.{Api, UserCoding}
import typedapi.server._
import typedapi.server.akkahttp._
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import cats.implicits._
import org.specs2.concurrent.ExecutionEnv

import scala.concurrent.{Future, Await}
import scala.concurrent.duration._

final class AkkaHttpServerSupportSpec(implicit ee: ExecutionEnv) extends ServerSupportSpec[Future]()(catsStdInstancesForFuture(ee.ec)) {

  import UserCoding._
  import FailFastCirceSupport._

  implicit val timeout = 5.second
  implicit val system  = ActorSystem("akka-http-server-spec", defaultExecutionContext = Some(ee.ec))
  implicit val mat     = ActorMaterializer()

  import system.dispatcher

  val endpoints = deriveAll[Future](Api).from(
    path, 
    segment, 
    query, 
    header, 
    fixed, 
    input, 
    clientHdr,
    coll,
    matching, 
    send, 
    get, 
    put, 
    putB, 
    post, 
    postB, 
    delete, 
    code200, 
    code400, 
    code500
  )
  val sm        = ServerManager(Http(), "localhost", 9000)
  val server    = mount(sm, endpoints)

  "akka http implements TypedApi's server interface" >> {
    tests(9000)

    step {
      Await.ready(server.map(_.unbind()), timeout)
      Await.ready(system.terminate, timeout)
    }
  }

}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/server/Http4sServerSupportSpec.scala
================================================
package http.support.tests.server

import http.support.tests.{UserCoding, Api}
import typedapi.server._
import typedapi.server.http4s._
import cats.effect.IO
import org.http4s.server.blaze.BlazeBuilder

final class Http4sServerSupportSpec extends ServerSupportSpec[IO] {

  import UserCoding._

  val endpoints = deriveAll[IO](Api).from(
    path, 
    segment, 
    query, 
    header, 
    fixed, 
    input, 
    clientHdr,
    coll,
    matching, 
    send, 
    get, 
    put, 
    putB, 
    post, 
    postB, 
    delete, 
    code200, 
    code400, 
    code500
  )
  val sm        = ServerManager(BlazeBuilder[IO], "localhost", 9000)
  val server    = mount(sm, endpoints).unsafeRunSync()

  "http4s implements TypedApi's server interface" >> {
    tests(9000)

    step {
      server.shutdown.unsafeRunSync()
    }
  }

}


================================================
FILE: http-support-tests/src/test/scala/http/support/tests/server/ServerSupportSpec.scala
================================================
package http.support.tests.server

import typedapi.server._
import http.support.tests.{User, UserCoding}
import org.http4s._
import org.http4s.dsl.io._
import org.http4s.client.blaze._
import org.http4s.client.dsl.io._
import cats.Applicative
import cats.effect.IO
import org.specs2.mutable.Specification

import scala.language.higherKinds

abstract class ServerSupportSpec[F[_]: Applicative] extends Specification {

  import StatusCodes._

  sequential

  val client = Http1Client[IO]().unsafeRunSync

  def tests(port: Int) = {
    import UserCoding._

    "paths and segments" >> {
      client.expect[User](s"http://localhost:$port/path").unsafeRunSync() === User("path", 27)
      client.expect[User](s"http://localhost:$port/segment/jim").unsafeRunSync() === User("jim", 27)
    }

    "queries" >> {
      client.expect[User](s"http://localhost:$port/query?age=42").unsafeRunSync() === User("query", 42)
    }

    "headers" >> {
      client.expect[User](Request[IO](
        method = GET,
        uri = Uri.fromString(s"http://localhost:$port/header").right.get,
        headers = Headers(Header("age", "42"))
      )).unsafeRunSync() === User("header", 42)
      client.expect[User](Request[IO](
        method = GET,
        uri = Uri.fromString(s"http://localhost:$port/header/fixed").right.get,
        headers = Headers(Header("Hello", "*"))
      )).unsafeRunSync() === User("fixed", 27)
      client.expect[User](Request[IO](
        method = GET,
        uri = Uri.fromString(s"http://localhost:$port/header/client").right.get
      )).unsafeRunSync() === User("client header", 27)
      client.expect[User](Request[IO](
        method = GET,
        uri = Uri.fromString(s"http://localhost:$port/header/input/client").right.get
      )).unsafeRunSync() === User("input", 27)
      client.fetch[Option[Header]](
        Request[IO](
          method = GET,
          uri = Uri.fromString(s"http://localhost:$port/header/server/send").right.get
        )
      )(
        resp => IO {
          resp.headers.toList.find(_.name.toString == "Hello")
        }
      ).unsafeRunSync() === Some(Header("Hello", "*"))
      client.expect[User](Request[IO](
        method = GET,
        uri = Uri.fromString(s"http://localhost:$port/header/server/match").right.get,
        headers = Headers(Header("test", "foo"), Header("testy", "bar"), Header("meh", "NONO"))
      )).unsafeRunSync() === User("test -> foo,testy -> bar", 27)
      client.fetch[Option[Header]](
        Request[IO](
          method = OPTIONS,
          uri = Uri.fromString(s"http://localhost:$port/header/fixed").right.get,
          headers = Headers(Header("Hello", "*"))
        )
      )(
        resp => IO {
          resp.headers.toList.find(_.name.toString == "Access-Control-Allow-Methods")
        }
      ).unsafeRunSync() === Some(Header("Access-Control-Allow-Methods", "GET"))
    }

    "methods" >> {
      client.expect[User](s"http://localhost:$port/").unsafeRunSync() === User("get", 27)
      client.expect[User](PUT(Uri.fromString(s"http://localhost:$port/").right.get)).unsafeRunSync() === User("put", 27)
      client.expect[User](PUT(Uri.fromString(s"http://localhost:$port/body").right.get, User("joe", 27))).unsafeRunSync() === User("joe", 27)
      client.expect[User](POST(Uri.fromString(s"http://localhost:$port/").right.get)).unsafeRunSync() === User("post", 27)
      client.expect[User](POST(Uri.fromString(s"http://localhost:$port/body").right.get, User("joe", 27))).unsafeRunSync() === User("joe", 27)
      client.expect[User](DELETE(Uri.fromString(s"http://localhost:$port/?reasons=because").right.get)).unsafeRunSync() === User("because", 27)
    }

    "status codes" >> {
      client.fetch[Int](GET(Uri.fromString(s"http://localhost:$port/status/200").right.get))(resp => IO.pure(resp.status.code)).unsafeRunSync === 200
      client.fetch[Int](GET(Uri.fromString(s"http://localhost:$port/status/400").right.get))(resp => IO.pure(resp.status.code)).unsafeRunSync === 400
      client.fetch[Int](GET(Uri.fromString(s"http://localhost:$port/status/500").right.get))(resp => IO.pure(resp.status.code)).unsafeRunSync === 500
    }
  }

  val path: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("path", 27)))
  val segment: String => F[Result[User]] = name => Applicative[F].pure(successWith(Ok)(User(name, 27)))
  val query: Int => F[Result[User]] = age => Applicative[F].pure(successWith(Ok)(User("query", age)))
  val header: Int => F[Result[User]] = age => Applicative[F].pure(successWith(Ok)(User("header", age)))
  val fixed: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("fixed", 27)))
  val input: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("input", 27)))
  val clientHdr: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("client header", 27)))
  val coll: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("coll", 27)))
  val matching: Map[String, String] => F[Result[User]] = matches => Applicative[F].pure(successWith(Ok)(User(matches.mkString(","), 27)))
  val send: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("send", 27)))
  val get: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("get", 27)))
  val put: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("put", 27)))
  val putB: User => F[Result[User]] = user => Applicative[F].pure(successWith(Ok)(user))
  val post: () => F[Result[User]] = () => Applicative[F].pure(successWith(Ok)(User("post", 27)))
  val postB: User => F[Result[User]] = user => Applicative[F].pure(successWith(Ok)(user))
  val delete: List[String] => F[Result[User]] = reasons => {
    Applicative[F].pure(successWith(Ok)(User(reasons.mkString(","), 27)))
  }
  val code200: () => F[Result[String]] = () => Applicative[F].pure(successWith(Ok)(""))
  val code400: () => F[Result[String]] = () => Applicative[F].pure(errorWith(BadRequest, "meh"))
  val code500: () => F[Result[String]] = () => Applicative[F].pure(errorWith(InternalServerError, "boom"))
}


================================================
FILE: http4s-client/src/main/scala/typedapi/client/http4s/package.scala
================================================
package typedapi.client

import cats.{Monad, MonadError, Applicative}
import org.http4s._
import org.http4s.client._
import org.http4s.Status.Successful

import scala.language.higherKinds

package object http4s {

  private implicit class Http4sRequestOps[F[_]](req: Request[F]) {

    def withQuery(queries: Map[String, List[String]]): Request[F] = {
      if (queries.nonEmpty) {
        val q   = org.http4s.Query.fromMap(queries)
        val uri = Uri(req.uri.scheme, req.uri.authority, req.uri.path, q, req.uri.fragment)

        req.withUri(uri)
      }
      else req
    }

    def withHeaders(headers: Map[String, String]): Request[F] = {
      if (headers.nonEmpty) {
        val h: List[Header] = headers.map { case (k, v) => org.http4s.Header(k, v) }(collection.breakOut)

        req.withHeaders(Headers(h))
      }
      else req
    }

    def run(cm: ClientManager[Client[F]])(implicit F: Applicative[F]): F[Response[F]] =
      cm.client.fetch(req)(resp => F.pure(resp))
  }

  private implicit class Http4sResponseOps[F[_]](resp: Response[F]) {

    def decode[A](implicit d: EntityDecoder[F, A], F: MonadError[F, Throwable]): F[A] = resp match {
      case Successful(_resp) =>
        d.decode(_resp, strict = false).fold(throw _, identity)
      case failedResponse =>
        F.raiseError(UnexpectedStatus(failedResponse.status))
    }
  }

  implicit def rawGetRequest[F[_]](implicit F: Applicative[F]) = new RawGetRequest[Client[F], F] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[Resp] = {
      val request = Request[F](Method.GET, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)

      request.run(cm)
    }
  }

  implicit def getRequest[F[_], A](implicit decoder: EntityDecoder[F, A], F: MonadError[F, Throwable]) = new GetRequest[Client[F], F, A] {
    private val raw = rawGetRequest[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, cm))(_.decode[A])
  }

  implicit def rawPutRequest[F[_]](implicit F: Applicative[F]) = new RawPutRequest[Client[F], F] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[Resp] = {
      val request = Request[F](Method.PUT, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)

      request.run(cm)
    }
  }

  implicit def putRequest[F[_], A](implicit decoder: EntityDecoder[F, A], F: MonadError[F, Throwable]) = new PutRequest[Client[F], F, A] {
    private val raw = rawPutRequest[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, cm))(_.decode[A])
  }

  implicit def rawPutBodyRequest[F[_], Bd](implicit encoder: EntityEncoder[F, Bd], F: Monad[F]) = new RawPutWithBodyRequest[Client[F], F, Bd] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Client[F]]): F[Resp] = {
      val requestF = Request[F](Method.PUT, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)
        .withBody(body)

      F.flatMap(requestF)(_.run(cm))
    }
  }

  implicit def putBodyRequest[F[_], Bd, A](implicit encoder: EntityEncoder[F, Bd], 
                                                    decoder: EntityDecoder[F, A], 
                                                    F: MonadError[F, Throwable]) = new PutWithBodyRequest[Client[F], F, Bd, A] {
    private val raw = rawPutBodyRequest[F, Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, body, cm))(_.decode[A])
  }

  implicit def rawPostRequest[F[_]](implicit F: Applicative[F]) = new RawPostRequest[Client[F], F] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[Resp] = {
      val request = Request[F](Method.POST, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)

      request.run(cm)
    }
  }

  implicit def postRequest[F[_], A](implicit decoder: EntityDecoder[F, A], F: MonadError[F, Throwable]) = new PostRequest[Client[F], F, A] {
    private val raw = rawPostRequest[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, cm))(_.decode[A])
  }

  implicit def rawPostBodyRequest[F[_], Bd](implicit encoder: EntityEncoder[F, Bd], 
                                                     F: Monad[F]) = new RawPostWithBodyRequest[Client[F], F, Bd] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Client[F]]): F[Resp] = {
      val requestF = Request[F](Method.POST, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)
        .withBody(body)

      F.flatMap(requestF)(_.run(cm))
    }
  }

  implicit def postBodyRequest[F[_], Bd, A](implicit encoder: EntityEncoder[F, Bd], 
                                                     decoder: EntityDecoder[F, A], 
                                                     F: MonadError[F, Throwable]) = new PostWithBodyRequest[Client[F], F, Bd, A] {
    private val raw = rawPostBodyRequest[F, Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, body, cm))(_.decode[A])
  }

  implicit def rawDeleteRequest[F[_]](implicit F: Applicative[F]) = new RawDeleteRequest[Client[F], F] {
    type Resp = Response[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[Resp] = {
      val request = Request[F](Method.DELETE, Uri.unsafeFromString(deriveUriString(cm, uri)))
        .withQuery(queries)
        .withHeaders(headers)

      request.run(cm)
    }
  }

  implicit def deleteRequest[F[_], A](implicit decoder: EntityDecoder[F, A], F: MonadError[F, Throwable]) = new DeleteRequest[Client[F], F, A] {
    private val raw = rawDeleteRequest[F]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Client[F]]): F[A] =
      F.flatMap(raw(uri, queries, headers, cm))(_.decode[A])
  }
}


================================================
FILE: http4s-server/src/main/scala/typedapi/server/htt4ps/package.scala
================================================
package typedapi.server

import typedapi.shared.MethodType
import cats.Monad
import cats.implicits._
import cats.effect.IO
import org.http4s._
import org.http4s.dsl._
import org.http4s.dsl.impl.EntityResponseGenerator
import org.http4s.server.Server
import org.http4s.server.blaze.BlazeBuilder
import shapeless._
import shapeless.ops.hlist.Prepend

import scala.language.higherKinds

package object http4s {

  private def getHeaders(raw: Map[String, String]): List[Header.Raw] = 
    raw.map { case (key, value) => Header(key, value) }(collection.breakOut)

  implicit def noReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, F[_]: Monad, FOut](implicit encoder: EntityEncoder[F, FOut]) = 
    new NoReqBodyExecutor[El, KIn, VIn, M, F, FOut] {
      type R   = Request[F]
      type Out = F[Response[F]]

      private val dsl = Http4sDsl[F]
      import dsl._

      private def respGenerator(code: Status) = new EntityResponseGenerator[F] { val status = code }

      def apply(req: R, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, VIn, F, FOut]): Either[ExtractionError, Out] = {
        extract(eReq, endpoint).map { extracted =>
          Monad[F].flatMap(execute(extracted, endpoint)) {
            case Right((code, response)) =>
              respGenerator(Status(code.statusCode))(response, getHeaders(endpoint.headers): _*)

            case Left(HttpError(code, msg)) =>
              respGenerator(Status(code.statusCode))(msg, getHeaders(endpoint.headers): _*)
          }
        }
      }
    }

  implicit def withReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, Bd, M <: MethodType, ROut <: HList, POut <: HList, F[_]: Monad, FOut]
    (implicit encoder: EntityEncoder[F, FOut], 
              decoder: EntityDecoder[F, Bd],
              _prepend: Prepend.Aux[ROut, Bd :: HNil, POut], 
              _eqProof: POut =:= VIn) = new ReqBodyExecutor[El, KIn, VIn, Bd, M, ROut, POut, F, FOut] {
    type R   = Request[F]
    type Out = F[Response[F]]

    implicit val prepend = _prepend
    implicit val eqProof = _eqProof

    private val dsl = Http4sDsl[F]
    import dsl._

    private def respGenerator(code: Status) = new EntityResponseGenerator[F] { val status = code }

    def apply(req: R, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, (BodyType[Bd], ROut), F, FOut]): Either[ExtractionError, Out] = {
      extract(eReq, endpoint).map { case (_, extracted) =>
        for {
          body     <- req.as[Bd]
          response <- execute(extracted, body, endpoint).flatMap {
            case Right((code, response)) =>
              respGenerator(Status(code.statusCode))(response, getHeaders(endpoint.headers): _*)

            case Left(HttpError(code, msg)) =>
              respGenerator(Status(code.statusCode))(msg, getHeaders(endpoint.headers): _*)
          }
        } yield response
      }
    }
  }

  implicit val mountEndpoints = new MountEndpoints[BlazeBuilder[IO], Request[IO], IO[Response[IO]]] {

    import io._

    type Out = IO[Server[IO]]

    def apply(server: ServerManager[BlazeBuilder[IO]], endpoints: List[Serve[Request[IO], IO[Response[IO]]]]): Out = {
      val service = HttpService[IO] {
        case request =>
          def execute(eps: List[Serve[Request[IO], IO[Response[IO]]]], eReq: EndpointRequest): IO[Response[IO]] = eps match {
            case collection.immutable.::(endpoint, tail) => endpoint(request, eReq) match {
              case Right(response)            => response
              case Left(RouteNotFound)        => execute(tail, eReq)
              case Left(BadRouteRequest(msg)) => io.BadRequest(msg)
            }

            case Nil => io.NotFound("uri = " + request.uri)
          }

          val eReq = EndpointRequest(
            request.method.name, 
            {
              val path = request.uri.path.split("/")

              if (path.isEmpty) List.empty
              else              path.tail.toList
            },
            request.uri.multiParams.map { case (key, value) => key -> value.toList },
            request.headers.toList.map(header => header.name.toString.toLowerCase -> header.value)(collection.breakOut)
          )

          if (request.method.name == "OPTIONS") {
            IO(Response(headers = Headers(getHeaders(optionsHeaders(endpoints, eReq)))))
          }
          else
            execute(endpoints, eReq)
      }

      server.server.bindHttp(server.port, server.host).mountService(service, "/").start
    }
  }
}


================================================
FILE: js-client/src/main/scala/typedapi/client/js/package.scala
================================================
package typedapi.client

import typedapi.util._
import org.scalajs.dom.XMLHttpRequest
import org.scalajs.dom.ext.Ajax

import scala.concurrent.{Future, ExecutionContext}

package object js {

  private def renderQueries(queries: Map[String, List[String]]): String = 
    if (queries.nonEmpty)
      queries
        .map { case (key, values) => s"$key=${values.mkString(",")}" }
        .mkString("?", "&", "")
  else
    ""

  private def flatten[A](decoded: Future[Either[Exception, A]])(implicit ec: ExecutionContext): Future[A] = decoded.flatMap {
    case Right(a)    => Future.successful(a)
    case Left(error) => Future.failed(error)
  }

  implicit def rawGetRequest(implicit ec: ExecutionContext) = new RawGetRequest[Ajax.type, Future] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[Resp] =
      cm.client
        .get(
          url     = deriveUriString(cm, uri) + renderQueries(queries),
          headers = headers
        )
  }

  implicit def getRequest[A](implicit decoder: Decoder[Future, A], ec: ExecutionContext) = new GetRequest[Ajax.type, Future, A] {
    private val raw = rawGetRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, cm).flatMap(response => flatten(decoder(response.responseText)))
  }

  implicit def rawPutRequest(implicit ec: ExecutionContext) = new RawPutRequest[Ajax.type, Future] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[Resp] =
      cm.client
        .put(
          url     = deriveUriString(cm, uri) + renderQueries(queries),
          headers = headers
        )
  }

  implicit def putRequest[A](implicit decoder: Decoder[Future, A], ec: ExecutionContext) = new PutRequest[Ajax.type, Future, A] {
    private val raw = rawPutRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, cm).flatMap(response => flatten(decoder(response.responseText)))
  }

  implicit def rawPutBodyRequest[Bd](implicit encoder: Encoder[Future, Bd], ec: ExecutionContext) = new RawPutWithBodyRequest[Ajax.type, Future, Bd] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Ajax.type]): Future[Resp] =
      encoder(body).flatMap { encoded => 
        cm.client
          .put(
            url     = deriveUriString(cm, uri) + renderQueries(queries),
            headers = headers,
            data    = encoded
          )
      }
  }

  implicit def putBodyRequest[Bd, A](implicit encoder: Encoder[Future, Bd], decoder: Decoder[Future, A], ec: ExecutionContext) = new PutWithBodyRequest[Ajax.type, Future, Bd, A] {
    private val raw = rawPutBodyRequest[Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, body, cm).flatMap(response => flatten(decoder(response.responseText)))
  }

  implicit def rawPostRequest(implicit ec: ExecutionContext) = new RawPostRequest[Ajax.type, Future] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[Resp] =
      cm.client
        .post(
          url     = deriveUriString(cm, uri) + renderQueries(queries),
          headers = headers
        )
  }

  implicit def postRequest[A](implicit decoder: Decoder[Future, A], ec: ExecutionContext) = new PostRequest[Ajax.type, Future, A] {
    private val raw = rawPostRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, cm).flatMap(response => flatten(decoder(response.responseText)))
  }

  implicit def rawPostBodyRequest[Bd](implicit encoder: Encoder[Future, Bd], ec: ExecutionContext) = new RawPostWithBodyRequest[Ajax.type, Future, Bd] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Ajax.type]): Future[Resp] =
      encoder(body).flatMap { encoded =>
        cm.client
          .post(
            url     = deriveUriString(cm, uri) + renderQueries(queries),
            headers = headers,
            data    = encoded
          )
      }
  }

  implicit def postBodyRequest[Bd, A](implicit encoder: Encoder[Future, Bd], decoder: Decoder[Future, A], ec: ExecutionContext) = new PostWithBodyRequest[Ajax.type, Future, Bd, A] {
    private val raw = rawPostBodyRequest[Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, body, cm).flatMap(response => flatten(decoder(response.responseText)))
  }

  implicit def rawDeleteRequest(implicit ec: ExecutionContext) = new RawDeleteRequest[Ajax.type, Future] {
    type Resp = XMLHttpRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[Resp] =
      cm.client
        .delete(
          url     = deriveUriString(cm, uri) + renderQueries(queries),
          headers = headers
        )
  }

  implicit def deleteRequest[A](implicit decoder: Decoder[Future, A], ec: ExecutionContext) = new DeleteRequest[Ajax.type, Future, A] {
    private val raw = rawDeleteRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Ajax.type]): Future[A] =
      raw(uri, queries, headers, cm).flatMap(response => flatten(decoder(response.responseText)))
  }
}


================================================
FILE: project/build.properties
================================================
sbt.version=1.0.4

================================================
FILE: project/build.scala
================================================
import sbt._

object Dependencies {

  private val specs2V = "3.9.4"

  val shared = Seq(
    "com.chuusai" %% "shapeless" % "2.3.3" % Compile,

    "org.specs2"  %% "specs2-core" % specs2V % Test
  )

  val client = Seq(
    "org.specs2"  %% "specs2-core" % specs2V % Test
  )

  val server = Seq(
    "org.specs2"  %% "specs2-core" % specs2V % Test
  )

  private val http4sV = "0.18.0"

  val http4sClient = Seq(
    "org.http4s" %% "http4s-blaze-client" % http4sV % Provided,
  )

  val http4sServer = Seq(
    "org.http4s" %% "http4s-blaze-server" % http4sV % Provided,
    "org.http4s" %% "http4s-dsl" % http4sV % Provided
  )

  private val akkaHttpV = "10.0.13"

  val akkaHttpClient = Seq(
    "com.typesafe.akka" %% "akka-http" % akkaHttpV % Provided,
    "com.typesafe.akka" %% "akka-http-core" % akkaHttpV % Provided
  )

  val akkaHttpServer = Seq(
    "com.typesafe.akka" %% "akka-http" % akkaHttpV % Provided,
    "com.typesafe.akka" %% "akka-http-core" % akkaHttpV % Provided
  )

  private val scalajHttpV = "2.4.1"

  val scalajHttpClient = Seq(
    "org.scalaj" %% "scalaj-http" % scalajHttpV % Provided
  )

  private val circeV = "0.9.1"

  val httpSupportTests = Seq(
    "org.specs2" %% "specs2-core" % specs2V % Test,

    "org.http4s" %% "http4s-blaze-client" % http4sV % Test,
    "org.http4s" %% "http4s-blaze-server" % http4sV % Test,
    "org.http4s" %% "http4s-dsl" % http4sV          % Test,
    "org.http4s" %% "http4s-circe" % http4sV        % Test,
    "com.typesafe.akka" %% "akka-http" % akkaHttpV  % Test,
    "org.scalaj" %% "scalaj-http" % scalajHttpV     % Test,

    "io.circe"   %% "circe-core" % circeV               % Test,
    "io.circe"   %% "circe-parser" % circeV             % Test,
    "io.circe"   %% "circe-generic" % circeV            % Test,
    "de.heikoseeberger" %% "akka-http-circe" % "1.21.0" % Test,

    "org.specs2" %% "specs2-core" % specs2V         % Test
  )

  val ammoniteSupport = Seq(
    "org.scalaj" %% "scalaj-http" % scalajHttpV % Compile
  )
}


================================================
FILE: project/plugins.sbt
================================================
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3")

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22")


================================================
FILE: scalaj-http-client/src/main/scala/typedapi/client/scalajhttp/package.scala
================================================
package typedapi.client

import typedapi.util._
import scalaj.http._

package object scalajhttp {

  type Id[A]       = A
  type Blocking[A] = Either[Exception, A]

  private def reduceQueries(queries: Map[String, List[String]]): Map[String, String] = 
    queries.map { case (key, values) => key -> values.mkString(",") }(collection.breakOut)

  implicit def rawGetRequest = new RawGetRequest[Http.type, Id] {
    type Resp = HttpResponse[String]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).method("GET")

      req.asString
    }
  }

  implicit def getRequest[A](implicit decoder: Decoder[Id, A]) = new GetRequest[Http.type, Blocking, A] {
    private val raw = rawGetRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, cm).body)
  }

  implicit def rawPutRequest = new RawPutRequest[Http.type, Id] {
    type Resp = HttpResponse[String]
    
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).method("PUT")

      req.asString
    }
  }

  implicit def putRequest[A](implicit decoder: Decoder[Id, A]) = new PutRequest[Http.type, Blocking, A] {
    private val raw = rawPutRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, cm).body)
  }

  implicit def rawPutBodyRequest[Bd](implicit encoder: Encoder[Id, Bd]) = new RawPutWithBodyRequest[Http.type, Id, Bd] {
    type Resp = HttpResponse[String]
    
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).put(encoder(body))

      req.asString
    }
  }

  implicit def putBodyRequest[Bd, A](implicit encoder: Encoder[Id, Bd], decoder: Decoder[Id, A]) = new PutWithBodyRequest[Http.type, Blocking, Bd, A] {
    private val raw = rawPutBodyRequest[Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, body, cm).body)
  }

  implicit def rawPostRequest = new RawPostRequest[Http.type, Id] {
    type Resp = HttpResponse[String]
    
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).method("POST")

      req.asString
    }
  }

  implicit def postRequest[A](implicit decoder: Decoder[Id, A]) = new PostRequest[Http.type, Blocking, A] {
    private val raw = rawPostRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, cm).body)
  }

  implicit def rawPostBodyRequest[Bd](implicit encoder: Encoder[Id, Bd]) = new RawPostWithBodyRequest[Http.type, Id, Bd] {
    type Resp = HttpResponse[String]
    
    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).postData(encoder(body))

      req.asString
    }
  }

  implicit def postBodyRequest[Bd, A](implicit encoder: Encoder[Id, Bd], decoder: Decoder[Id, A]) = new PostWithBodyRequest[Http.type, Blocking, Bd, A] {
    private val raw = rawPostBodyRequest[Bd]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd, cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, body, cm).body)
  }

  implicit def rawDeleteRequest = new RawDeleteRequest[Http.type, Id] {
    type Resp = HttpResponse[String]

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Id[Resp] = {
      val req = cm.client(deriveUriString(cm, uri)).params(reduceQueries(queries)).headers(headers).method("DELETE")

      req.asString
    }
  }

  implicit def deleteRequest[A](implicit decoder: Decoder[Id, A]) = new DeleteRequest[Http.type, Blocking, A] {
    private val raw = rawDeleteRequest

    def apply(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], cm: ClientManager[Http.type]): Blocking[A] =
      decoder(raw(uri, queries, headers, cm).body)
  }
}


================================================
FILE: server/src/main/scala/typedapi/server/Endpoint.scala
================================================
package typedapi.server

import typedapi.shared._
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.function._

import scala.language.higherKinds

/** Represents a server endpoint and is basically a function which gets the expected input `VIn` and returns the expected output. */
abstract class Endpoint[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], Out]
    (val method: String, val extractor: RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut], val headers: Map[String, String]) {

  def apply(in: VIn): F[Result[Out]]
}

/** Request representation which every server implementation has to provide. */
final case class EndpointRequest(method: String, 
                                 uri: List[String],
                                 queries: Map[String, List[String]],
                                 headers: Map[String, String])

final class ExecutableDerivation[F[_]] {

  final class Derivation[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, Fn, Out]
    (extractor: RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut],
     method: String,
     headers: Map[String, String],
     fnToVIn: FnToProduct.Aux[Fn, VIn => F[Result[Out]]]) {

    /** Restricts type of parameter `fn` to a function defined by the given API:
      * 
      * {{{
      * val Api = := :> Segment[String]('name) :> Get[User]
      * 
      * derive[IO](Api).from(name: String => IO.pure(User(name)))
      * }}}
      */
    def from(fn: Fn): Endpoint[El, KIn, VIn, M, ROut, F, Out] =
      new Endpoint[El, KIn, VIn, M, ROut, F, Out](method, extractor, headers) {
        private val fin = fnToVIn(fn)

        def apply(in: VIn): F[Result[Out]] = fin(in)
      }
  }

  def apply[H <: HList, FH <: HList, El <: HList, KIn <: HList, VIn <: HList, ROut, Fn, M <: MethodType, MT <: MediaType, Out]
    (apiList: ApiTypeCarrier[H])
    (implicit filter: FilterClientElements.Aux[H, FH],
              folder: Lazy[TypeLevelFoldLeft.Aux[FH, Unit, (El, KIn, VIn, M, FieldType[MT, Out])]],
              extractor: RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut],
              methodShow: MethodToString[M],
              serverHeaders: ServerHeaderExtractor[El],
              inToFn: Lazy[FnFromProduct.Aux[VIn => F[Result[Out]], Fn]],
              fnToVIn: Lazy[FnToProduct.Aux[Fn, VIn => F[Result[Out]]]]): Derivation[El, KIn, VIn, M, ROut, Fn, Out] =
    new Derivation[El, KIn, VIn, M, ROut, Fn, Out](extractor, methodShow.show, serverHeaders(Map.empty), fnToVIn.value)
}


================================================
FILE: server/src/main/scala/typedapi/server/EndpointComposition.scala
================================================
package typedapi.server

import typedapi.shared._
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.function._

import scala.language.higherKinds
import scala.annotation.implicitNotFound

/** Fuses [[RouteExtractor]] and the endpoint function into an [[Endpoint]]. */
trait EndpointConstructor[F[_], Fn, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, Out] {

  def apply(fn: Fn): Endpoint[El, KIn, VIn, M, ROut, F, Out]
}

/** Compiles RouteExtractor and FunApply for every API endpoint and generates expected list of endpoint functions. */
@implicitNotFound("""Could not precompile your API. This can happen when you try to extract an value from the route which is not supported (ValueExtractor in RouteExtractor.scala)
 
transformed: ${H}""")
sealed trait PrecompileEndpoint[F[_], H <: HList] {

  // list of expected endpoint functions
  type Fns <: HList
  // list of endpoint constructors
  type Consts <: HList

  def constructors: Consts
}

object PrecompileEndpoint extends PrecompileEndpointLowPrio {

  type Aux[F[_], H <: HList, Fns0 <: HList, Consts0 <: HList] = PrecompileEndpoint[F, H] {
    type Fns    = Fns0
    type Consts = Consts0
  }
}

trait PrecompileEndpointLowPrio {

  implicit def hnilPrecompiledCase[F[_]] = new PrecompileEndpoint[F, HNil] {
    type Fns    = HNil
    type Consts = HNil

    val constructors = HNil
  }

  implicit def constructorsCase[F[_], Fn, El <: HList, KIn <: HList, VIn <: HList, MT <: MediaType, Out, M <: MethodType, ROut, T <: HList]
    (implicit extractor: RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut],
              methodShow: MethodToString[M],
              serverHeaders: ServerHeaderExtractor[El],
              vinToFn: FnFromProduct.Aux[VIn => F[Result[Out]], Fn],
              fnToVIn: Lazy[FnToProduct.Aux[Fn, VIn => F[Result[Out]]]],
              next: PrecompileEndpoint[F, T]) =
    new PrecompileEndpoint[F, (El, KIn, VIn, M, FieldType[MT, Out]) :: T] {
      type Fns    = Fn :: next.Fns
      type Consts = EndpointConstructor[F, Fn, El, KIn, VIn, M, ROut, Out] :: next.Consts

      val constructor = new EndpointConstructor[F, Fn, El, KIn, VIn, M, ROut, Out] {
        def apply(fn: Fn): Endpoint[El, KIn, VIn, M, ROut, F, Out] = new Endpoint[El, KIn, VIn, M, ROut, F, Out](methodShow.show, extractor, serverHeaders(Map.empty)) {
          private val fin = fnToVIn.value(fn)

          def apply(in: VIn): F[Result[Out]] = fin(in)
        }
      }

      val constructors = constructor :: next.constructors
    }
}

@implicitNotFound("""Whoops, you should not be here. This seems to be a bug.

constructors: ${Consts}
functions: ${Fns}""")
sealed trait MergeToEndpoint[F[_], Consts <: HList, Fns <: HList] {

  type Out <: HList

  def apply(constructors: Consts, fns: Fns): Out
}

object MergeToEndpoint extends MergeToEndpointLowPrio {

  type Aux[F[_], Consts <: HList, Fns <: HList, Out0 <: HList] = MergeToEndpoint[F, Consts, Fns] { type Out = Out0 }
}

trait MergeToEndpointLowPrio {

  implicit def hnilMergeCase[F[_]] = new MergeToEndpoint[F, HNil, HNil] {
    type Out = HNil

    def apply(constructors: HNil, fns: HNil): Out = HNil
  }

  implicit def mergeCase[F[_], El <: HList, KIn <: HList, VIn <: HList, Out0, M <: MethodType, ROut, Consts <: HList, Fn, Fns <: HList]
    (implicit next: MergeToEndpoint[F, Consts, Fns]) =
    new MergeToEndpoint[F, EndpointConstructor[F, Fn, El, KIn, VIn, M, ROut, Out0] :: Consts, Fn :: Fns] {
      type Out = Endpoint[El, KIn, VIn, M, ROut, F, Out0] :: next.Out

      def apply(constructors: EndpointConstructor[F, Fn, El, KIn, VIn, M, ROut, Out0] :: Consts, fns: Fn :: Fns): Out = {
        val endpoint = constructors.head(fns.head)

        endpoint :: next(constructors.tail, fns.tail)
      }
    }
}

final class ExecutableCompositionDerivation[F[_]] {

  final class Derivation[H <: HList, Fns <: HList, Consts <: HList, Out <: HList, Drv](pre: PrecompileEndpoint.Aux[F, H, Fns, Consts], 
                                                                                       merge: MergeToEndpoint.Aux[F, Consts, Fns, Out],
                                                                                       derived: FnFromProduct.Aux[Fns => Out, Drv]) {

    /** Restricts type of input parameter to a composition of functions defined by the precompile-stage.
      *
      * {{{
      * val Api =
      *   (:= :> Segment[String]('name) :> Get[User]) :|:
      *   (:= :> "foo" :> Segment[String]('name) :> Get[User])
      * 
      * val f0: String => IO[User] = name => IO.pure(User(name))
      * val f1: String => IO[User] = name => IO.pure(User(name))
      * deriveAll[IO](Api).from(f0 _, f1 _)
      * }}}
      */
    val from: Drv = derived.apply(fns => merge(pre.constructors, fns))
  }

  def apply[H <: HList, FH <: HList, Fold <: HList, Fns <: HList, FnsTup, Consts <: HList, Out <: HList, Drv](apiLists: CompositionCons[H])
                                      (implicit filter: FilterClientElementsList.Aux[H, FH],
                                                folder: TypeLevelFoldLeftList.Aux[FH, Fold],
                                                pre: PrecompileEndpoint.Aux[F, Fold, Fns, Consts],
                                                merge: MergeToEndpoint.Aux[F, Consts, Fns, Out],
                                                derived: FnFromProduct.Aux[Fns => Out, Drv]): Derivation[Fold, Fns, Consts, Out, Drv] =
    new Derivation[Fold, Fns, Consts, Out, Drv](pre, merge, derived)
}


================================================
FILE: server/src/main/scala/typedapi/server/EndpointExecutor.scala
================================================
package typedapi.server

import typedapi.shared.MethodType
import shapeless._
import shapeless.ops.hlist.Prepend

import scala.language.higherKinds
import scala.annotation.implicitNotFound

@implicitNotFound("""Cannot find EndpointExecutor. Do you miss some implicit values e.g. encoder/decoder?

elements: ${El}
input keys: ${KIn}
input values: ${VIn}""")
sealed trait EndpointExecutor[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], FOut] {

  type R
  type Out

  def extract(eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, ROut, F, FOut]): Either[ExtractionError, ROut] = 
    endpoint.extractor(eReq, HNil)

  def apply(req: R, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, ROut, F, FOut]): Either[ExtractionError, Out]
}

object EndpointExecutor {

  type Aux[R0, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], FOut, Out0] = EndpointExecutor[El, KIn, VIn, M, ROut, F, FOut] {
    type R   = R0
    type Out = Out0
  }
}

trait NoReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, F[_], FOut] extends EndpointExecutor[El, KIn, VIn, M, VIn, F, FOut] {

  protected def execute(input: VIn, endpoint: Endpoint[El, KIn, VIn, M, VIn, F, FOut]): F[Result[FOut]] = 
    endpoint.apply(input)
}

trait ReqBodyExecutor[El <: HList, KIn <: HList, VIn <: HList, Bd, M <: MethodType, ROut <: HList, POut <: HList, F[_], FOut] extends EndpointExecutor[El, KIn, VIn, M, (BodyType[Bd], ROut), F, FOut] {

  implicit def prepend: Prepend.Aux[ROut, Bd :: HNil, POut]
  implicit def eqProof: POut =:= VIn

  protected def execute(input: ROut, body: Bd, endpoint: Endpoint[El, KIn, VIn, M, (BodyType[Bd], ROut), F, FOut]): F[Result[FOut]] = 
    endpoint.apply(input :+ body)
}


================================================
FILE: server/src/main/scala/typedapi/server/FilterClientElements.scala
================================================
package typedapi.server

import typedapi.shared.{ClientHeaderElement, ClientHeaderParam, ClientHeaderCollParam}
import shapeless._

sealed trait FilterClientElements[H <: HList] {

  type Out <: HList
}

sealed trait FilterClientElementsLowPrio {

  implicit val filterClientResult = new FilterClientElements[HNil] {
    type Out = HNil
  }

  implicit def filterClientKeep[El, T <: HList](implicit next: FilterClientElements[T]) = new FilterClientElements[El :: T] {
    type Out = El :: next.Out
  }
}

object FilterClientElements extends FilterClientElementsLowPrio {

  type Aux[H <: HList, Out0 <: HList] = FilterClientElements[H] { type Out = Out0 }

  implicit def filterClientEl[K, V, T <: HList](implicit next: FilterClientElements[T]) = new FilterClientElements[ClientHeaderElement[K, V] :: T] {
    type Out = next.Out
  }

  implicit def filterClientParam[K, V, T <: HList](implicit next: FilterClientElements[T]) = new FilterClientElements[ClientHeaderParam[K, V] :: T] {
    type Out = next.Out
  }

  implicit def filterClientCollParam[V, T <: HList](implicit next: FilterClientElements[T]) = new FilterClientElements[ClientHeaderCollParam[V] :: T] {
    type Out = next.Out
  }
}

sealed trait FilterClientElementsList[H <: HList] {

  type Out <: HList
}

object FilterClientElementsList {

  type Aux[H <: HList, Out0 <: HList] = FilterClientElementsList[H] { type Out = Out0 }

  implicit val filterClientListResult = new FilterClientElementsList[HNil] {
    type Out = HNil
  }

  implicit def filterClientListStep[Api <: HList, T <: HList](implicit filtered: FilterClientElements[Api], next: FilterClientElementsList[T]) = 
    new FilterClientElementsList[Api :: T] {
      type Out = filtered.Out :: next.Out
    }
}


================================================
FILE: server/src/main/scala/typedapi/server/RouteExtractor.scala
================================================
package typedapi.server

import typedapi.shared._
import shapeless.{HList, HNil, Witness}
import shapeless.labelled.FieldType
import shapeless.ops.hlist.Reverse

import scala.util.Try
import scala.annotation.implicitNotFound

sealed trait ExtractionError
case object RouteNotFound extends ExtractionError
final case class BadRouteRequest(msg: String) extends ExtractionError

/** Builds a function which extracts inputs from a given requests based on the API. 
  *  - if a request path does not fit the API definition `RouteNotFound` is returned
  *  - if a query, header, body, etc is missing `BadRouteRequest` is returned
  */
@implicitNotFound("""Cannot find RouteExtractor. Maybe a ValueExtractor could not be found.

elements: ${El}
input keys: ${KIn}
inout values: ${VIn}
method: ${M}""")
trait RouteExtractor[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList] {

  type Out

  def apply(request: EndpointRequest, inAgg: EIn): Either[ExtractionError, Out]
}

object RouteExtractor extends RouteExtractorMediumPrio {

  type Aux[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList, Out0] = RouteExtractor[El, KIn, VIn, M, EIn] { type Out = Out0 }

  type Extract[Out] = Either[ExtractionError, Out]

  def NotFoundE[Out]: Extract[Out] = Left(RouteNotFound)
  def BadRequestE[Out](msg: String): Extract[Out] = Left(BadRouteRequest(msg))
}

trait RouteExtractorLowPrio {

  import RouteExtractor._

  implicit def pathExtractor[S, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[S], show: WitnessToString[S], next: RouteExtractor[El, KIn, VIn, M, EIn]) =
    new RouteExtractor[shapeless.::[S, El], KIn, VIn, M, EIn] {
    type Out = next.Out

    def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = request.uri match {
      case p :: tail => 
        if (p == show.show(wit.value))
          next(request.copy(uri = tail), inAgg)
        else
          NotFoundE

      case Nil => NotFoundE
    }
  }
}

trait RouteExtractorMediumPrio extends RouteExtractorLowPrio {

  import RouteExtractor._

  private def checkEmptyPath[Out](request: EndpointRequest)(f: EndpointRequest => Extract[Out]): Extract[Out] = 
    if (request.uri.isEmpty)
      f(request)
    else
      NotFoundE

  implicit def segmentExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList](implicit value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[V, EIn]]) = 
    new RouteExtractor[shapeless.::[SegmentInput, El], shapeless.::[K, KIn], shapeless.::[V, VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = request.uri match {
        case p :: tail => value(p).fold(NotFoundE[Out])(v => next(request.copy(uri = tail), v :: inAgg))
        case Nil       => NotFoundE
      }
    }

  implicit def queryExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[V, EIn]]) =
    new RouteExtractor[shapeless.::[QueryInput, El], shapeless.::[K, KIn], shapeless.::[V, VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key = show.show(wit.value)

        req.queries.get(key).fold(BadRequestE[Out](s"missing query '$key'")) { raw =>
          raw.headOption.flatMap(value.apply).fold(BadRequestE[Out](s"query '$key' has not type ${value.typeDesc}")) { v =>
            next(request, v :: inAgg)
          }
        }
      }
    }

  implicit def queryOptListExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[Option[V], EIn]]) =
    new RouteExtractor[shapeless.::[QueryInput, El], shapeless.::[K, KIn], shapeless.::[Option[V], VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key = show.show(wit.value)

        req.queries.get(key).fold(next(request, None :: inAgg)) { raw =>
          raw.headOption.flatMap(value.apply).fold(BadRequestE[Out](s"query '$key' has not type ${value.typeDesc}")) { v =>
            next(request, Some(v) :: inAgg)
          }
        }
      }
    }

  implicit def queryListExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[List[V], EIn]]) =
    new RouteExtractor[shapeless.::[QueryInput, El], shapeless.::[K, KIn], shapeless.::[List[V], VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key = show.show(wit.value)

        req.queries.get(key).fold(next(request, Nil :: inAgg)) { raw =>
          val vs = raw.flatMap(value.apply)

          if (vs.length < raw.length)
            BadRequestE(s"some values of query '$key' are no ${value.typeDesc}")
          else
            next(request, vs :: inAgg)
        }
      }
    }

  implicit def headerExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[V, EIn]]) =
    new RouteExtractor[shapeless.::[HeaderInput, El], shapeless.::[K, KIn], shapeless.::[V, VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key = show.show(wit.value).toLowerCase

        req.headers.get(key).fold(BadRequestE[Out](s"missing header '$key'")) { raw =>
          value(raw).fold(BadRequestE[Out](s"header '$key' has not type ${value.typeDesc}")) { v =>
            next(request, v :: inAgg)
          }
        }
      }
    }

  implicit def headerOptExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[Option[V], EIn]]) =
    new RouteExtractor[shapeless.::[HeaderInput, El], shapeless.::[K, KIn], shapeless.::[Option[V], VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key = show.show(wit.value)

        req.headers.get(key.toLowerCase).fold(next(request, None :: inAgg)) { raw =>
          value(raw).fold(BadRequestE[Out](s"header '$key' has not type ${value.typeDesc}")) { v =>
            next(request, Some(v) :: inAgg)
          }
        }
      }
    }

  implicit def fixedHeaderExtractor[K, V, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit kWit: Witness.Aux[K], kShow: WitnessToString[K], vWit: Witness.Aux[V], vShow: WitnessToString[V], next: RouteExtractor[El, KIn, VIn, M, EIn]) =
    new RouteExtractor[shapeless.::[FixedHeader[K, V], El], KIn, VIn, M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key   = kShow.show(kWit.value)
        val value = vShow.show(vWit.value)

        req.headers.get(key.toLowerCase).fold(BadRequestE[Out](s"missing header '$key'")) { raw =>
          if (raw != value)
            BadRequestE[Out](s"header '$key' has unexpected value '${raw}' - expected '${value}'")
          else
            next(request, inAgg)
        }
      }
    }

  implicit def ignoreServerHeaderSendExtractor[K, V, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit next: RouteExtractor[El, KIn, VIn, M, EIn]) =
    new RouteExtractor[shapeless.::[ServerHeaderSend[K, V], El], KIn, VIn, M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        next(request, inAgg)
      }
    }

  implicit def serverHeaderMatchExtractor[El <: HList, K, V, KIn <: HList, VIn <: HList, M <: MethodType, EIn <: HList]
      (implicit wit: Witness.Aux[K], show: WitnessToString[K], value: ValueExtractor[V], next: RouteExtractor[El, KIn, VIn, M, shapeless.::[Map[String, V], EIn]]) =
    new RouteExtractor[shapeless.::[ServerHeaderMatchInput, El], shapeless.::[K, KIn], shapeless.::[Map[String, V], VIn], M, EIn] {
      type Out = next.Out

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        val key      = show.show(wit.value).toLowerCase
        val matchesE = req.headers.foldLeft[Either[ExtractionError, Map[String, V]]](Right(Map.empty)) { 
          case (Right(agg), (k, raw)) =>
            if (k.contains(key)) {
              value(raw).fold(BadRequestE[Map[String, V]](s"header '$k' has not type ${value.typeDesc}")) { v =>
                Right(agg + (k -> v))
              }
            }
            else
              Right(agg)
          case (error, _) => error
        }

        matchesE.fold(
          error   => Left(error),
          matches => next(request, matches :: inAgg)
        )
        
      }
    }

  implicit def getExtractor[EIn <: HList, REIn <: HList](implicit rev: Reverse.Aux[EIn, REIn]) = new RouteExtractor[HNil, HNil, HNil, GetCall, EIn] {
    type Out = REIn

    def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
      if (req.method == "GET" || req.method == "OPTIONS") 
        Right(inAgg.reverse)
      else 
        NotFoundE
    }
  }

  implicit def putExtractor[EIn <: HList, REIn <: HList](implicit rev: Reverse.Aux[EIn, REIn]) = new RouteExtractor[HNil, HNil, HNil, PutCall, EIn] {
    type Out = REIn

    def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
      if (req.method == "PUT" || req.method == "OPTIONS") 
        Right(inAgg.reverse)
      else 
        NotFoundE
    }
  }

  implicit def putWithBodyExtractor[BMT <: MediaType, Bd, EIn <: HList] = 
    new RouteExtractor[HNil, shapeless.::[FieldType[BMT, BodyField.T], HNil], shapeless.::[Bd, HNil], PutWithBodyCall, EIn] {
      type Out = (BodyType[Bd], EIn)

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        if (req.method == "PUT" || req.method == "OPTIONS")
          Right((BodyType[Bd], inAgg))
        else
          NotFoundE
      }
    }

  implicit def postExtractor[EIn <: HList] = new RouteExtractor[HNil, HNil, HNil, PostCall, EIn] {
    type Out = EIn

    def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
      if (req.method == "POST" || req.method == "OPTIONS") 
        Right(inAgg)
      else 
        NotFoundE
    }
  }

  implicit def postWithBodyExtractor[BMT <: MediaType, Bd, EIn <: HList, REIn <: HList] = 
    new RouteExtractor[HNil, shapeless.::[FieldType[BMT, BodyField.T], HNil], shapeless.::[Bd, HNil], PostWithBodyCall, EIn] {
      type Out = (BodyType[Bd], EIn)

      def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
        if (req.method == "POST" || req.method == "OPTIONS")
          Right((BodyType[Bd], inAgg))
        else
          NotFoundE
      }
    }

  implicit def deleteExtractor[EIn <: HList] = new RouteExtractor[HNil, HNil, HNil, DeleteCall, EIn] {
    type Out = EIn

    def apply(request: EndpointRequest, inAgg: EIn): Extract[Out] = checkEmptyPath(request) { req =>
      if (req.method == "DELETE" || req.method == "OPTIONS")
        Right(inAgg)
      else 
        NotFoundE
    }
  }
}

trait ValueExtractor[A] extends (String => Option[A]) {

  def typeDesc: String
}

object ValueExtractor extends ValueExtractorInstances

trait ValueExtractorInstances {

  def extract[A](f: String => A, _typeDesc: String) = new ValueExtractor[A] {
    val typeDesc = _typeDesc

    def apply(raw: String): Option[A] = Try(f(raw)).toOption
  }

  implicit val booleanExtractor = extract[Boolean](_.toBoolean, "Boolean")
  implicit val byteExtractor    = extract[Byte](_.toByte, "Byte")
  implicit val shortExtractor   = extract[Short](_.toShort, "Short")
  implicit val intExtractor     = extract[Int](_.toInt, "Int")
  implicit val longExtractor    = extract[Long](_.toLong, "Long")
  implicit val floatExtractor   = extract[Float](_.toFloat, "Float")
  implicit val doubleExtractor  = extract[Double](_.toDouble, "Double")
  implicit val stringExtractor  = extract[String](identity, "String")
}

final case class BodyType[Bd]()


================================================
FILE: server/src/main/scala/typedapi/server/Serve.scala
================================================
package typedapi.server

/** Reduces an Endpoint and its EndpointExecutor to a simple Request => Response function. */
trait Serve[Req, Resp] {

  def options(eReq: EndpointRequest): Option[(String, Map[String, String])]
  def apply(req: Req, eReq: EndpointRequest): Either[ExtractionError, Resp]
}


================================================
FILE: server/src/main/scala/typedapi/server/ServeToList.scala
================================================
package typedapi.server

import shapeless._

import scala.annotation.implicitNotFound

@implicitNotFound("""Cannot find ServeToList instance to map Serve HList to List. Maybe you have different Request, Response types defined?. 

serves: ${H}
request: ${Req}
response: ${Resp}""")
sealed trait ServeToList[H <: HList, Req, Resp] {

  def apply(h: H): List[Serve[Req, Resp]]
}

object ServeToList extends ServeToListLowPrio

trait ServeToListLowPrio {

  implicit def hnilToList[Req, Resp] = new ServeToList[HNil, Req, Resp] {
    def apply(h: HNil): List[Serve[Req, Resp]] = Nil
  }

  implicit def serveToList[Req, Resp, T <: HList](implicit next: ServeToList[T, Req, Resp]) = new ServeToList[Serve[Req, Resp] :: T, Req, Resp] {
    def apply(h: Serve[Req, Resp] :: T): List[Serve[Req, Resp]] = h.head :: next(h.tail)
  }
}


================================================
FILE: server/src/main/scala/typedapi/server/ServerHeaderExtractor.scala
================================================
package typedapi.server

import typedapi.shared._
import shapeless._

import scala.annotation.implicitNotFound

@implicitNotFound("""Whoops, you should not be here. This seems to be a bug.

elements: ${El}""")
sealed trait ServerHeaderExtractor[El <: HList] {

  def apply(agg: Map[String, String]): Map[String, String]
}

sealed trait ServerHeaderExtractorLowPrio {

  implicit val serverHeaderReturnCase = new ServerHeaderExtractor[HNil] {
    def apply(agg: Map[String, String]): Map[String, String] = agg
  }

  implicit def serverHeaderIgnoreCase[H, T <: HList](implicit next: ServerHeaderExtractor[T]) = new ServerHeaderExtractor[H :: T] {
    def apply(agg: Map[String, String]): Map[String, String] = next(agg)
  }
}

object ServerHeaderExtractor extends ServerHeaderExtractorLowPrio {

  implicit def serverHeaderExtractCase[K, V, T <: HList]
      (implicit kWit: Witness.Aux[K], kShow: WitnessToString[K], vWit: Witness.Aux[V], vShow: WitnessToString[V], next: ServerHeaderExtractor[T]) = 
    new ServerHeaderExtractor[ServerHeaderSend[K, V] :: T] {
      def apply(agg: Map[String, String]): Map[String, String] = {
        val key   = kShow.show(kWit.value)
        val value = vShow.show(vWit.value)

        next(agg + (key -> value))
      }
    }
}


================================================
FILE: server/src/main/scala/typedapi/server/ServerManager.scala
================================================
package typedapi.server

import scala.annotation.tailrec

final case class ServerManager[S](server: S, host: String, port: Int)

object ServerManager {

  def mount[S, Req, Resp, Out](server: ServerManager[S], endpoints: List[Serve[Req, Resp]])(implicit mounting: MountEndpoints.Aux[S, Req, Resp, Out]): Out =
    mounting(server, endpoints)
}

trait MountEndpoints[S, Req, Resp] {

  type Out

  final def optionsHeaders(eps: List[Serve[Req, Resp]], eReq: EndpointRequest): Map[String, String] = {
    @tailrec
    def collect(serve: List[Serve[Req, Resp]], methods: List[String], headers: Map[String, String]): (List[String], Map[String, String]) = serve match {
      case collection.immutable.::(endpoint, tail) => endpoint.options(eReq) match {
        case Some((method, hds)) => collect(tail, method :: methods, hds ++ headers)
        case _                   => collect(tail, methods, headers)
      }

      case Nil => (methods, headers)
    }

    val (methods, headers) = collect(eps, Nil, Map.empty)
    
    headers + (("Access-Control-Allow-Methods", methods.mkString(",")))
  }

  def apply(server: ServerManager[S], endpoints: List[Serve[Req, Resp]]): Out
}

object MountEndpoints {

  type Aux[S, Req, Resp, Out0] = MountEndpoints[S, Req, Resp] { type Out = Out0 }
}


================================================
FILE: server/src/main/scala/typedapi/server/StatusCodes.scala
================================================
package typedapi.server

final case class SuccessCode(statusCode: Int) extends AnyVal
final case class ErrorCode(statusCode: Int) extends AnyVal

final case class HttpError(code: ErrorCode, message: String)

object StatusCodes {

  // success codes
  final val Continue           = SuccessCode(100)
  final val SwitchingProtocols = SuccessCode(101)
  final val Processing         = SuccessCode(102)

  final val Ok                          = SuccessCode(200)
  final val Created                     = SuccessCode(201)
  final val Accepted                    = SuccessCode(202)
  final val NonAuthoritativeInformation = SuccessCode(203)
  final val NoContent                   = SuccessCode(204)
  final val ResetContent                = SuccessCode(205)
  final val PartialContent              = SuccessCode(206)
  final val MultiStatus                 = SuccessCode(207)
  final val AlreadyReported             = SuccessCode(208)
  final val IMUsed                      = SuccessCode(226)

  final val MultipleChoices   = SuccessCode(300)
  final val MovedPermanently  = SuccessCode(301)
  final val Found             = SuccessCode(302)
  final val SeeOther          = SuccessCode(303)
  final val NotModified       = SuccessCode(304)
  final val UseProxy          = SuccessCode(305)
  final val TemporaryRedirect = SuccessCode(307)
  final val PermanentRedirect = SuccessCode(308)

  // error codes
  final val BadRequest                      = ErrorCode(400)
  final val Unauthorized                    = ErrorCode(401)
  final val PaymentRequired                 = ErrorCode(402)
  final val Forbidden                       = ErrorCode(403)
  final val NotFound                        = ErrorCode(404)
  final val MethodNotAllowed                = ErrorCode(405)
  final val NotAcceptable                   = ErrorCode(406)
  final val ProxyAuthenticationRequired     = ErrorCode(407)
  final val RequestTimeout                  = ErrorCode(408)
  final val Conflict                        = ErrorCode(409)
  final val Gone                            = ErrorCode(410)
  final val LengthRequired                  = ErrorCode(411)
  final val PreconditionFailed              = ErrorCode(412)
  final val PayloadTooLarge                 = ErrorCode(413)
  final val RequestURITooLong               = ErrorCode(414)
  final val UnsupportedMediaType            = ErrorCode(415)
  final val RequestedRangeNotSatisfiable    = ErrorCode(416)
  final val ExpectationFailed               = ErrorCode(417)
  final val ImAteapot                       = ErrorCode(418)
  final val MisdirectedRequest              = ErrorCode(421)
  final val UnprocessableEntity             = ErrorCode(422)
  final val Locked                          = ErrorCode(423)
  final val FailedDependency                = ErrorCode(424)
  final val UpgradeRequired                 = ErrorCode(426)
  final val PreconditionRequired            = ErrorCode(428)
  final val TooManyRequests                 = ErrorCode(429)
  final val RequestHeaderFieldsTooLarge     = ErrorCode(431)
  final val ConnectionClosedWithoutResult = ErrorCode(444)
  final val UnavailableForLegalReasons      = ErrorCode(451)
  final val ClientClosedRequest             = ErrorCode(499)

  final val InternalServerError           = ErrorCode(500)
  final val NotImplemented                = ErrorCode(501)
  final val BadGateway                    = ErrorCode(502)
  final val ServiceUnavailable            = ErrorCode(503)
  final val GatewayTimeout                = ErrorCode(504)
  final val HTTPVersionNotSupported       = ErrorCode(505)
  final val VariantAlsoNegotiates         = ErrorCode(506)
  final val InsufficientStorage           = ErrorCode(507)
  final val LoopDetected                  = ErrorCode(508)
  final val NotExtended                   = ErrorCode(510)
  final val NetworkAuthenticationRequired = ErrorCode(511)
  final val NetworkConnectTimeoutError    = ErrorCode(599)
}


================================================
FILE: server/src/main/scala/typedapi/server/package.scala
================================================
package typedapi

import typedapi.shared._
import shapeless._
import shapeless.ops.hlist.Mapper

import scala.language.higherKinds

package object server extends TypeLevelFoldLeftLowPrio 
                      with TypeLevelFoldLeftListLowPrio 
                      with WitnessToStringLowPrio
                      with ApiTransformer {

  val SC = StatusCodes

  type Result[A] = Either[HttpError, (SuccessCode, A)]

  def successWith[A](code: SuccessCode)(a: A): Result[A] = Right(code -> a)
  def success[A](a: A): Result[A] = successWith(StatusCodes.Ok)(a)

  def errorWith[A](code: ErrorCode, message: String): Result[A] = Left(HttpError(code, message))

  def derive[F[_]]: ExecutableDerivation[F] = new ExecutableDerivation[F]

  def mount[S, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], FOut, Req, Resp, Out]
      (server: ServerManager[S], endpoint: Endpoint[El, KIn, VIn, M, ROut, F, FOut])
      (implicit executor: EndpointExecutor.Aux[Req, El, KIn, VIn, M, ROut, F, FOut, Resp], mounting: MountEndpoints.Aux[S, Req, Resp, Out]): Out =
    mounting(server, List(new Serve[executor.R, executor.Out] {
      def options(eReq: EndpointRequest): Option[(String, Map[String, String])] = {
        endpoint.extractor(eReq, HNil) match {
          case Right(_) => Some((endpoint.method, endpoint.headers))
          case _        => None
        }
      }

      def apply(req: executor.R, eReq: EndpointRequest): Either[ExtractionError, executor.Out] = executor(req, eReq, endpoint)
    }))

  def deriveAll[F[_]]: ExecutableCompositionDerivation[F] = new ExecutableCompositionDerivation[F]

  object endpointToServe extends Poly1 {

    implicit def default[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], FOut](implicit executor: EndpointExecutor[El, KIn, VIn, M, ROut, F, FOut]) = 
      at[Endpoint[El, KIn, VIn, M, ROut, F, FOut]] { endpoint =>
        new Serve[executor.R, executor.Out] {
          def options(eReq: EndpointRequest): Option[(String, Map[String, String])] = {
            endpoint.extractor(eReq, HNil) match {
              case Right(_) => Some((endpoint.method, endpoint.headers))
              case _        => None
            }
          }

          def apply(req: executor.R, eReq: EndpointRequest): Either[ExtractionError, executor.Out] = executor(req, eReq, endpoint)
        }
      }
  }

  def mount[S, End <: HList, Serv <: HList, Req, Resp, Out](server: ServerManager[S], end: End)(implicit mapper: Mapper.Aux[endpointToServe.type, End, Serv], toList: ServeToList[Serv, Req, Resp], mounting: MountEndpoints.Aux[S, Req, Resp, Out]): Out =
    mounting(server, toList(end.map(endpointToServe)))
}


================================================
FILE: server/src/test/scala/typedapi/server/ApiToEndpointLinkSpec.scala
================================================
package typedapi.server

import typedapi.dsl._
import shapeless._

import org.specs2.mutable.Specification

final class ApiToEndpointLinkSpec extends Specification {

  import StatusCodes._

  case class Foo(name: String)

  "link api definitions to endpoint functions" >> { 
    val Api = := :> "find" :> typedapi.dsl.Segment[String]('name) :> 
                    Query[Int]('limit) :> 
                    Client.Header('hello, 'world) :> 
                    Server.Send('foo, 'bar) :> Server.Match[String]("hi") :>
                    Get[Json, List[Foo]]

    val endpoint0 = derive[Option](Api).from((name, limit, hi) => Some(successWith(Ok)(List(Foo(name)).take(limit))))
    endpoint0("john" :: 10 :: Map("hi" -> "whats", "hi-ho" -> "up") :: HNil) === Some(Right(Ok -> List(Foo("john"))))
    endpoint0.headers == Map("foo" -> "bar")
    endpoint0.method == "GET"
  }
}


================================================
FILE: server/src/test/scala/typedapi/server/RouteExtractorSpec.scala
================================================
package typedapi.server

import typedapi.dsl._
import typedapi.shared._
import shapeless.{HList, HNil, Lazy}
import org.specs2.mutable.Specification

final class RouteExtractorSpec extends Specification {

  case class Foo(name: String)

  def extract[H <: HList, El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, Out]
    (api: ApiTypeCarrier[H])
    (implicit folder: Lazy[TypeLevelFoldLeft.Aux[H, Unit, (El, KIn, VIn, M, Out)]],
              extractor: RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut]): RouteExtractor.Aux[El, KIn, VIn, M, HNil, ROut] = extractor

  "determine routes defined by requests and extract included data (segments, queries, headers)" >> {
    "no data" >> {
      val ext = extract(:= :> "hello" :> "world" :> Get[Json, Foo])
      
      ext(EndpointRequest("GET", List("hello", "world"), Map.empty, Map.empty), HNil) === Right(HNil)
      ext(EndpointRequest("GET", List("hello", "wrong"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext(EndpointRequest("GET", List("hello"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext(EndpointRequest("GET", Nil, Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
    }

    "segments" >> {
      val ext = extract(:= :> "foo" :> Segment[Int]('age) :> Get[Json, Foo])

      ext(EndpointRequest("GET", List("foo", "0"), Map.empty, Map.empty), HNil) === Right(0 :: HNil)
      ext(EndpointRequest("GET", List("foo", "wrong"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext(EndpointRequest("GET", Nil, Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
    }

    "queries" >> {
      val ext0 = extract(:= :> "foo" :> Query[Int]('age) :> Get[Json, Foo])

      ext0(EndpointRequest("GET", List("foo"), Map("age" -> List("0")), Map.empty), HNil) === Right(0 :: HNil)
      ext0(EndpointRequest("GET", List("foo"), Map("age" -> List("wrong")), Map.empty), HNil) === RouteExtractor.BadRequestE("query 'age' has not type Int")
      ext0(EndpointRequest("GET", List("foo"), Map("wrong" -> List("0")), Map.empty), HNil) === RouteExtractor.BadRequestE("missing query 'age'")
      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === RouteExtractor.BadRequestE("missing query 'age'")
      ext0(EndpointRequest("GET", List("foo", "bar"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE

      val ext1 = extract(:= :> "foo" :> Query[Option[Int]]('age) :> Get[Json, Foo])

      ext1(EndpointRequest("GET", List("foo"), Map("age" -> List("0")), Map.empty), HNil) === Right(Some(0) :: HNil)
      ext1(EndpointRequest("GET", List("foo"), Map("wrong" -> List("0")), Map.empty), HNil) === Right(None :: HNil)

      val ext2 = extract(:= :> "foo" :> Query[List[Int]]('age) :> Get[Json, Foo])

      ext2(EndpointRequest("GET", List("foo"), Map("age" -> List("0", "1")), Map.empty), HNil) === Right(List(0, 1) :: HNil)
      ext2(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === Right(Nil :: HNil)
    }

    "headers" >> {
      val ext0 = extract(:= :> "foo" :> Header[Int]('age) :> Get[Json, Foo])

      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map("age" -> "0")), HNil) === Right(0 :: HNil)
      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map("age" -> "wrong")), HNil) === RouteExtractor.BadRequestE("header 'age' has not type Int")
      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map("wrong" -> "0")), HNil) === RouteExtractor.BadRequestE("missing header 'age'")
      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === RouteExtractor.BadRequestE("missing header 'age'")
      ext0(EndpointRequest("GET", List("foo", "bar"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE

      val ext2 = extract(:= :> "foo" :> Header[Option[Int]]('age) :> Get[Json, Foo])

      ext2(EndpointRequest("GET", List("foo"), Map.empty, Map("age" -> "0")), HNil) === Right(Some(0) :: HNil)
      ext2(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === Right(None :: HNil)

      val ext3 = extract(:= :> "foo" :> Header("Accept", "*") :> Get[Json, Foo])

      ext3(EndpointRequest("GET", List("foo"), Map.empty, Map("accept" -> "*")), HNil) === Right(HNil)
      ext3(EndpointRequest("GET", List("foo"), Map.empty, Map("wrong" -> "*")), HNil) === RouteExtractor.BadRequestE("missing header 'Accept'")
      ext3(EndpointRequest("GET", List("foo"), Map.empty, Map("accept" -> "wrong")), HNil) === RouteExtractor.BadRequestE("header 'Accept' has unexpected value 'wrong' - expected '*'")

      val ext4 = extract(:= :> "foo" :> Server.Send("Accept", "*") :> Get[Json, Foo])

      ext4(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === Right(HNil)

      val ext6 = extract(:= :> "foo" :> Server.Match[Int]("age") :> Get[Json, Foo])

      ext6(EndpointRequest("GET", List("foo"), Map.empty, Map("age" -> "0")), HNil) === Right(Map("age" ->0) :: HNil)
      ext6(EndpointRequest("GET", List("foo"), Map.empty, Map("age-and-what-not" -> "0")), HNil) === Right(Map("age-and-what-not" -> 0) :: HNil)
      ext6(EndpointRequest("GET", List("foo"), Map.empty, Map("nope" -> "0")), HNil) === Right(Map.empty :: HNil)
      ext6(EndpointRequest("GET", List("foo"), Map.empty, Map("age-" -> "hello")), HNil) === RouteExtractor.BadRequestE("header 'age-' has not type Int")
    }

    "body type" >> {
      val ext0 = extract(:= :> ReqBody[Json, Foo] :> Put[Json, Foo])

      ext0(EndpointRequest("PUT", Nil, Map.empty, Map.empty), HNil) === Right((BodyType[Foo], HNil))
      ext0(EndpointRequest("PUT", List("foo", "bar"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext0(EndpointRequest("OPTIONS", List("foo", "bar"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE

      val ext1 = extract(:= :> ReqBody[Json, Foo] :> Post[Json, Foo])

      ext1(EndpointRequest("POST", Nil, Map.empty, Map.empty), HNil) === Right((BodyType[Foo], HNil))
      ext1(EndpointRequest("OPTIONS", Nil, Map.empty, Map.empty), HNil) === Right((BodyType[Foo], HNil))
    }

    "methods" >> {
      val ext0 = extract(:= :> Get[Json, Foo])

      ext0(EndpointRequest("GET", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
      ext0(EndpointRequest("WRONG", Nil, Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext0(EndpointRequest("GET", List("foo"), Map.empty, Map.empty), HNil) === RouteExtractor.NotFoundE
      ext0(EndpointRequest("OPTIONS", Nil, Map.empty, Map.empty), HNil) === Right(HNil)

      val ext1 = extract(:= :> Put[Json, Foo])

      ext1(EndpointRequest("PUT", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
      ext1(EndpointRequest("OPTIONS", Nil, Map.empty, Map.empty), HNil) === Right(HNil)

      val ext2 = extract(:= :> Post[Json, Foo])

      ext2(EndpointRequest("POST", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
      ext2(EndpointRequest("OPTIONS", Nil, Map.empty, Map.empty), HNil) === Right(HNil)

      val ext3 = extract(:= :> Delete[Json, Foo])

      ext3(EndpointRequest("DELETE", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
      ext3(EndpointRequest("OPTIONS", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
    }

    "combinations" >> {
      val ext0 = extract(:= :> "foo" :> Query[Int]('age) :> Header[String]('id) :> Get[Json, Foo])

      ext0(EndpointRequest("GET", List("foo"), Map("age" -> List("0")), Map("id" -> "john")), HNil) === Right(0 :: "john" :: HNil)

      val ext1 = extract(:= :> Get[Json, Foo])

      ext1(EndpointRequest("GET", Nil, Map.empty, Map.empty), HNil) === Right(HNil)
    }
  }
}


================================================
FILE: server/src/test/scala/typedapi/server/ServeAndMountSpec.scala
================================================
package typedapi.server

import typedapi.dsl._
import typedapi.shared.MethodType
import shapeless.{HList, HNil, ::}
import shapeless.ops.hlist.{Prepend, Mapper}
import org.specs2.mutable.Specification

import scala.language.higherKinds

final class ServeAndMountSpec extends Specification {

  import StatusCodes._

  case class Foo(name: String)

  sealed trait Req
  case class TestRequest(uri: List[String], queries: Map[String, List[String]], headers: Map[String, String]) extends Req
  case class TestRequestWithBody[Bd](uri: List[String], queries: Map[String, List[String]], headers: Map[String, String], body: Bd) extends Req

  case class TestResponse(raw: Any)

  implicit def execNoBodyId[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, FOut] = 
    new NoReqBodyExecutor[El, KIn, VIn, M, Option, FOut] {
      type R = Req
      type Out = TestResponse

      def apply(req: Req, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, VIn, Option, FOut]): Either[ExtractionError, Out] =
        extract(eReq, endpoint).right.map { extracted => 
          TestResponse(execute(extracted, endpoint))
        }
    }

  implicit def execWithBody[El <: HList, KIn <: HList, VIn <: HList, Bd, M <: MethodType, ROut <: HList, POut <: HList, FOut](implicit _prepend: Prepend.Aux[ROut, Bd :: HNil, POut], _eqProof: POut =:= VIn) = 
    new ReqBodyExecutor[El, KIn, VIn, Bd, M, ROut, POut, Option, FOut] {
      type R = Req
      type Out = TestResponse

      implicit val prepend = _prepend
      implicit def eqProof = _eqProof

      def apply(req: Req, eReq: EndpointRequest, endpoint: Endpoint[El, KIn, VIn, M, (BodyType[Bd], ROut), Option, FOut]): Either[ExtractionError, Out] =
        extract(eReq, endpoint).right.map { case (_, extracted) => 
          TestResponse(execute(extracted, req.asInstanceOf[TestRequestWithBody[Bd]].body, endpoint))
        }
    }

  def toList[El <: HList, KIn <: HList, VIn <: HList, M <: MethodType, ROut, F[_], FOut](endpoint: Endpoint[El, KIn, VIn, M, ROut, F, FOut])
                                                                                        (implicit executor: EndpointExecutor[El, KIn, VIn, M, ROut, F, FOut]): List[Serve[executor.R, executor.Out]] = 
    List(new Serve[executor.R, executor.Out] {
      def options(eReq: EndpointRequest): Option[(String, Map[String, String])] = {
        endpoint.extractor(eReq, HNil) match {
          case Right(_) => Some((endpoint.method, endpoint.headers))
          case _        => None
        }
      }

      def apply(req: executor.R, eReq: EndpointRequest): Either[ExtractionError, executor.Out] = executor(req, eReq, endpoint)
    })

  def toList[End <: HList, Serv <: HList](end: End)(implicit mapper: Mapper.Aux[endpointToServe.type, End, Serv], s: ServeToList[Serv, Req, TestResponse]): List[Serve[Req, TestResponse]] =
    s(end.map(endpointToServe))

  "serve endpoints as simple Request -> Response functions and mount them into a server" >> {
    "serve single endpoint and no body" >> {
      val Api      = := :> "find" :> "user" :> Segment[String]('name) :> Query[Int]('sortByAge) :> Get[Json, List[Foo]]
      val endpoint = derive[Option](Api).from((name, sortByAge) => Some(successWith(Ok)(List(Foo(name)))))
      val served   = toList(endpoint)

      val req  = TestRequest(List("find", "user", "joe"), Map("sortByAge" -> List("1")), Map.empty)
      val eReq = EndpointRequest("GET", req.uri, req.queries, req.headers)

      served.head(req, eReq) === Right(TestResponse(Some(Right(Ok -> List(Foo("joe"))))))
    }

    "check if route exists and return method" >> {
      val Api      = := :> "find" :> "user" :> Segment[String]('name) :> Query[Int]('sortByAge) :> Server.Send("Hello", "*") :> Get[Json, List[Foo]]
      val endpoint = derive[Option](Api).from((name, sortByAge) => Some(successWith(Ok)(List(Foo(name)))))
      val served   = toList(endpoint)

      val req0  = TestRequest(List("find", "user", "joe"), Map("sortByAge" -> List("1")), Map.empty)
      val eReq0 = EndpointRequest("GET", req0.uri, req0.queries, req0.headers)

      served.head.options(eReq0) === Some(("GET", Map(("Hello", "*"))))

      val eReq1 = EndpointRequest("POST", req0.uri, req0.queries, req0.headers)

      served.head.options(eReq1) === None
    }

    "serve single endpoint and with body" >> {
      val Api      = := :> "find" :> "user" :> Segment[String]('name) :> ReqBody[Json, Foo] :> Post[Json, List[Foo]]
      val endpoint = derive[Option](Api).from((name, body) => Some(successWith(Ok)(List(Foo(name), body))))
      val served   = toList(endpoint)

      val req  = TestRequestWithBody(List("find", "user", "joe"), Map.empty, Map.empty, Foo("jim"))
      val eReq = EndpointRequest("POST", req.uri, req.queries, req.headers)

      served.head(req, eReq) === Right(TestResponse(Some(Right(Ok -> List(Foo("joe"), Foo("jim"))))))
    }

    "serve multiple endpoints" >> {
      val Api = 
        (:= :> "find" :> "user" :> Segment[String]('name) :> Query[Int]('sortByAge) :> Get[Json, List[Foo]]) :|:
        (:= :> "create" :> "user" :> ReqBody[Json, Foo] :> Post[Json, Foo])

      def find(name: String, age: Int): Option[Result[List[Foo]]] = Some(successWith(Ok)(List(Foo(name))))
      def create(foo: Foo): Option[Result[Foo]] = Some(successWith(Ok)(foo))

      val endpoints = deriveAll[Option](Api).from(find _, create _)

      val served = toList(endpoints)

      val req  = TestRequest(List("find", "user", "joe"), Map("sortByAge" -> List("1")), Map.empty)
      val eReq = EndpointRequest("GET", req.uri, req.queries, req.headers)

      served.head(req, eReq) === Right(TestResponse(Some(Right(Ok -> List(Foo("joe"))))))
    }
  }
}


================================================
FILE: shared/src/main/scala/typedapi/dsl/ApiDsl.scala
================================================
package typedapi.dsl

import typedapi.shared._
import shapeless._

sealed trait ApiList[H <: HList]

/** Basic operations. */
sealed trait MethodOps[H <: HList] {

  def :>[MT <: MediaType, A](body: TypeCarrier[ReqBodyElement[MT, A]]): WithBodyCons[MT, A, H] = WithBodyCons()
  def :>[M <: MethodElement](method: TypeCarrier[M]): ApiTypeCarrier[M :: H] = ApiTypeCarrier()
}

sealed trait PathOps[H <: HList] {

  def :>[S](path: Witness.Lt[S]): PathCons[PathElement[S] :: H] = PathCons()
  def :>[K, V](segment: TypeCarrier[SegmentParam[K, V]]): SegmentCons[SegmentParam[K, V] :: H] = SegmentCons()
}

sealed trait HeaderOps[H <: HList] {

  def :>[K, V](header: TypeCarrier[Header
Download .txt
gitextract_555nqngw/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── akka-http-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── akkahttp/
│                           └── package.scala
├── akka-http-server/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       └── akkahttp/
│                           └── package.scala
├── ammonite-client-support/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── package.scala
├── build.sbt
├── client/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── typedapi/
│       │           └── client/
│       │               ├── ApiRequest.scala
│       │               ├── ClientManager.scala
│       │               ├── ExecutableDerivation.scala
│       │               ├── ExecutablesFromHList.scala
│       │               ├── FilterServerElements.scala
│       │               ├── RequestDataBuilder.scala
│       │               ├── package.scala
│       │               └── test/
│       │                   ├── RequestInput.scala
│       │                   └── package.scala
│       └── test/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       ├── ClientManagerSpec.scala
│                       └── RequestDataBuilderSpec.scala
├── docs/
│   ├── ApiDefinition.md
│   ├── ClientCreation.md
│   ├── ExtendIt.md
│   ├── ServerCreation.md
│   └── example/
│       ├── ammonite_client_example.sc
│       ├── build.sbt
│       ├── client-js/
│       │   ├── index.html
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Client.scala
│       ├── client-jvm/
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Client.scala
│       ├── project/
│       │   ├── build.properties
│       │   └── plugins.sbt
│       ├── server/
│       │   └── src/
│       │       └── main/
│       │           └── scala/
│       │               └── Server.scala
│       └── shared/
│           └── src/
│               └── main/
│                   └── scala/
│                       ├── Apis.scala
│                       └── User.scala
├── http-support-tests/
│   └── src/
│       └── test/
│           └── scala/
│               └── http/
│                   └── support/
│                       └── tests/
│                           ├── User.scala
│                           ├── client/
│                           │   ├── AkkaHttpClientSupportSpec.scala
│                           │   ├── Http4sClientSupportSpec.scala
│                           │   ├── ScalajHttpClientSupportSpec.scala
│                           │   └── TestServer.scala
│                           ├── package.scala
│                           └── server/
│                               ├── AkkaHttpServerSupportSpec.scala
│                               ├── Http4sServerSupportSpec.scala
│                               └── ServerSupportSpec.scala
├── http4s-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── http4s/
│                           └── package.scala
├── http4s-server/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       └── htt4ps/
│                           └── package.scala
├── js-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── js/
│                           └── package.scala
├── project/
│   ├── build.properties
│   ├── build.scala
│   └── plugins.sbt
├── scalaj-http-client/
│   └── src/
│       └── main/
│           └── scala/
│               └── typedapi/
│                   └── client/
│                       └── scalajhttp/
│                           └── package.scala
├── server/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── typedapi/
│       │           └── server/
│       │               ├── Endpoint.scala
│       │               ├── EndpointComposition.scala
│       │               ├── EndpointExecutor.scala
│       │               ├── FilterClientElements.scala
│       │               ├── RouteExtractor.scala
│       │               ├── Serve.scala
│       │               ├── ServeToList.scala
│       │               ├── ServerHeaderExtractor.scala
│       │               ├── ServerManager.scala
│       │               ├── StatusCodes.scala
│       │               └── package.scala
│       └── test/
│           └── scala/
│               └── typedapi/
│                   └── server/
│                       ├── ApiToEndpointLinkSpec.scala
│                       ├── RouteExtractorSpec.scala
│                       └── ServeAndMountSpec.scala
└── shared/
    └── src/
        ├── main/
        │   └── scala/
        │       └── typedapi/
        │           ├── dsl/
        │           │   ├── ApiDsl.scala
        │           │   └── package.scala
        │           ├── package.scala
        │           ├── shared/
        │           │   ├── ApiElement.scala
        │           │   ├── ApiList.scala
        │           │   ├── ApiTransformer.scala
        │           │   ├── MediaTypes.scala
        │           │   ├── TypeCarrier.scala
        │           │   ├── TypeLevelFoldLeft.scala
        │           │   ├── WitnessToString.scala
        │           │   └── package.scala
        │           └── util/
        │               ├── Decoder.scala
        │               └── Encoder.scala
        └── test/
            └── scala/
                └── typedapi/
                    ├── ApiDefinitionSpec.scala
                    ├── SpecUtil.scala
                    ├── dsl/
                    │   └── ApiDslSpec.scala
                    └── shared/
                        └── ApiTransformerSpec.scala
Condensed preview — 81 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (269K chars).
[
  {
    "path": ".gitignore",
    "chars": 109,
    "preview": "# ensime\n*.*~\n.ensime\n.ensime_cache\nproject/ensime-plugin.sbt\n\n# sbt\ntarget/\nproject/target/\nproject/project/"
  },
  {
    "path": ".travis.yml",
    "chars": 199,
    "preview": "language: scala\n\nscala:\n  - 2.11.11\n  - 2.12.3\n\njdk:\n  - oraclejdk8\n\nscript:\n  - sbt ++$TRAVIS_SCALA_VERSION clean test\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3767,
    "preview": "### 0.2.0\n - `StatusCodes` and `MediaTypes` are in distinct `object`s, thus, have to be imported explicitly\n - improved "
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2018 Paul Heymann\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 6055,
    "preview": "[![Build Status](https://travis-ci.org/pheymann/typedapi.svg?branch=master)](https://travis-ci.org/pheymann/typedapi)\n[!"
  },
  {
    "path": "akka-http-client/src/main/scala/typedapi/client/akkahttp/package.scala",
    "chars": 13514,
    "preview": "package typedapi.client\n\nimport akka.http.scaladsl.HttpExt\nimport akka.http.scaladsl.model.RequestEntity\nimport akka.htt"
  },
  {
    "path": "akka-http-server/src/main/scala/typedapi/server/akkahttp/package.scala",
    "chars": 5415,
    "preview": "package typedapi.server\n\nimport typedapi.shared.MethodType\nimport shapeless._\nimport shapeless.ops.hlist.Prepend\nimport "
  },
  {
    "path": "ammonite-client-support/src/main/scala/typedapi/client/package.scala",
    "chars": 1490,
    "preview": "package typedapi.client\n\nimport typedapi.util._\nimport scalaj.http._\n\npackage object amm {\n\n  type Id[A]       = scalajh"
  },
  {
    "path": "build.sbt",
    "chars": 5565,
    "preview": "import sbt.Keys._\n\nval `compiler-2.12` = Seq(\n  \"-deprecation\",\n  \"-encoding\", \"utf-8\",\n  \"-explaintypes\",\n  \"-feature\","
  },
  {
    "path": "client/src/main/scala/typedapi/client/ApiRequest.scala",
    "chars": 3485,
    "preview": "package typedapi.client\n\nimport RequestDataBuilder.{Data, DataWithBody}\nimport typedapi.shared._\nimport shapeless._\n\nimp"
  },
  {
    "path": "client/src/main/scala/typedapi/client/ClientManager.scala",
    "chars": 503,
    "preview": "package typedapi.client\n\n/** Provides a supported client instance and some basic configuration. */\nfinal case class Clie"
  },
  {
    "path": "client/src/main/scala/typedapi/client/ExecutableDerivation.scala",
    "chars": 938,
    "preview": "package typedapi.client\n\nimport typedapi.shared.{MethodType, MediaType}\nimport shapeless._\nimport shapeless.labelled.Fie"
  },
  {
    "path": "client/src/main/scala/typedapi/client/ExecutablesFromHList.scala",
    "chars": 1314,
    "preview": "package typedapi.client\n\nimport typedapi.shared.{MethodType, MediaType}\nimport shapeless._\nimport shapeless.labelled.Fie"
  },
  {
    "path": "client/src/main/scala/typedapi/client/FilterServerElements.scala",
    "chars": 1558,
    "preview": "package typedapi.client\n\nimport typedapi.shared._\nimport shapeless._\n\n//TODO replace with Typelevelfoldleft\nsealed trait"
  },
  {
    "path": "client/src/main/scala/typedapi/client/RequestDataBuilder.scala",
    "chars": 12679,
    "preview": "package typedapi.client\n\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.labelled.FieldType\n\nimport scala.c"
  },
  {
    "path": "client/src/main/scala/typedapi/client/package.scala",
    "chars": 1571,
    "preview": "package typedapi\n\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.labelled.FieldType\nimport shapeless.ops.h"
  },
  {
    "path": "client/src/main/scala/typedapi/client/test/RequestInput.scala",
    "chars": 540,
    "preview": "package typedapi.client.test\n\nfinal case class ReqInput(method: String,\n                          uri: List[String],\n   "
  },
  {
    "path": "client/src/main/scala/typedapi/client/test/package.scala",
    "chars": 3827,
    "preview": "package typedapi.client\n\nimport scala.language.higherKinds\n\npackage object test {\n\n  type TestClientM = ClientManager[Un"
  },
  {
    "path": "client/src/test/scala/typedapi/client/ClientManagerSpec.scala",
    "chars": 272,
    "preview": "package typedapi.client\n\nimport org.specs2.mutable.Specification\n\nfinal class ClientManagerSpec extends Specification {\n"
  },
  {
    "path": "client/src/test/scala/typedapi/client/RequestDataBuilderSpec.scala",
    "chars": 6160,
    "preview": "package typedapi.client\n\nimport typedapi.dsl._\nimport typedapi.client.test._\n\nimport shapeless.Id\nimport org.specs2.muta"
  },
  {
    "path": "docs/ApiDefinition.md",
    "chars": 6699,
    "preview": "## How to define an API\nThe central idea behind Typedapi is to make client and server implementation as boilerplate-free"
  },
  {
    "path": "docs/ClientCreation.md",
    "chars": 4181,
    "preview": "## Create a client from your API\nAfter we [defined](https://github.com/pheymann/typedapi/blob/master/docs/ApiDefinition."
  },
  {
    "path": "docs/ExtendIt.md",
    "chars": 2196,
    "preview": "## Extend Typedapi to fit your needs\nYou ended up in this file if:\n - the default implements for a HTTP framework doesn'"
  },
  {
    "path": "docs/ServerCreation.md",
    "chars": 2024,
    "preview": "## Create a server from your API\nAfter we [defined](https://github.com/pheymann/typedapi/blob/master/docs/ApiDefinition."
  },
  {
    "path": "docs/example/ammonite_client_example.sc",
    "chars": 379,
    "preview": "import $ivy.`com.github.pheymann::typedapi-ammonite-client:0.2.0-M1`\n\nimport typedapi._\nimport client._\nimport amm._\n\nva"
  },
  {
    "path": "docs/example/build.sbt",
    "chars": 1823,
    "preview": "\nval typedapiVersion = \"0.2.0\"\nval http4sVersion   = \"0.18.0\"\n\nval commonSettings = Seq(\n  scalaVersion := \"2.12.4\"\n)\n\nl"
  },
  {
    "path": "docs/example/client-js/index.html",
    "chars": 127,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <body>\n    <script src=\"target/scala-2.12/client-js-fastopt.js\"></script>\n  </body>\n<"
  },
  {
    "path": "docs/example/client-js/src/main/scala/Client.scala",
    "chars": 1175,
    "preview": "\nimport typedapi.client._\nimport typedapi.client.js._\nimport org.scalajs.dom.ext.Ajax\nimport io.circe._\nimport io.circe."
  },
  {
    "path": "docs/example/client-jvm/src/main/scala/Client.scala",
    "chars": 772,
    "preview": "\nimport typedapi.client._\nimport typedapi.client.http4s._\nimport cats.effect.IO\nimport org.http4s._\nimport org.http4s.ci"
  },
  {
    "path": "docs/example/project/build.properties",
    "chars": 17,
    "preview": "sbt.version=1.0.4"
  },
  {
    "path": "docs/example/project/plugins.sbt",
    "chars": 56,
    "preview": "addSbtPlugin(\"org.scala-js\" % \"sbt-scalajs\" % \"0.6.22\")\n"
  },
  {
    "path": "docs/example/server/src/main/scala/Server.scala",
    "chars": 1523,
    "preview": "\nimport typedapi.server._\nimport typedapi.server.http4s._\nimport cats.effect.IO\nimport org.http4s._\nimport org.http4s.ci"
  },
  {
    "path": "docs/example/shared/src/main/scala/Apis.scala",
    "chars": 3832,
    "preview": "\nobject FromDsl {\n\n  import typedapi.dsl._\n\n  /* NOTE: we have to add the 'Access-Control-Allow-Origin' header to the se"
  },
  {
    "path": "docs/example/shared/src/main/scala/User.scala",
    "chars": 259,
    "preview": "\nimport io.circe.syntax._\nimport io.circe.generic.JsonCodec\n\nfinal case class User(name: String, age: Int)\n\nobject User "
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/User.scala",
    "chars": 387,
    "preview": "package http.support.tests\n\nimport cats.effect.IO\nimport io.circe.generic.semiauto._\nimport org.http4s.circe._\nimport or"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/client/AkkaHttpClientSupportSpec.scala",
    "chars": 2696,
    "preview": "package http.support.tests.client\n\nimport http.support.tests.{User, UserCoding, Api}\nimport typedapi.client._\nimport typ"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/client/Http4sClientSupportSpec.scala",
    "chars": 1895,
    "preview": "package http.support.tests.client\n\nimport http.support.tests.{UserCoding, User, Api}\nimport typedapi.client._\nimport typ"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/client/ScalajHttpClientSupportSpec.scala",
    "chars": 2252,
    "preview": "package http.support.tests.client\n\nimport http.support.tests.{UserCoding, User, Api}\nimport typedapi.client._\nimport typ"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/client/TestServer.scala",
    "chars": 2988,
    "preview": "package http.support.tests.client\n\nimport http.support.tests.{User, UserCoding}\nimport cats.effect.IO\nimport io.circe.sy"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/package.scala",
    "chars": 1334,
    "preview": "package http.support\n\nimport typedapi.dsl._\n\npackage object tests {\n\n  val Api =\n    (:= :> \"path\" :> Get[Json, User]) :"
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/server/AkkaHttpServerSupportSpec.scala",
    "chars": 1418,
    "preview": "package http.support.tests.server\n\nimport http.support.tests.{Api, UserCoding}\nimport typedapi.server._\nimport typedapi."
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/server/Http4sServerSupportSpec.scala",
    "chars": 833,
    "preview": "package http.support.tests.server\n\nimport http.support.tests.{UserCoding, Api}\nimport typedapi.server._\nimport typedapi."
  },
  {
    "path": "http-support-tests/src/test/scala/http/support/tests/server/ServerSupportSpec.scala",
    "chars": 6101,
    "preview": "package http.support.tests.server\n\nimport typedapi.server._\nimport http.support.tests.{User, UserCoding}\nimport org.http"
  },
  {
    "path": "http4s-client/src/main/scala/typedapi/client/http4s/package.scala",
    "chars": 7012,
    "preview": "package typedapi.client\n\nimport cats.{Monad, MonadError, Applicative}\nimport org.http4s._\nimport org.http4s.client._\nimp"
  },
  {
    "path": "http4s-server/src/main/scala/typedapi/server/htt4ps/package.scala",
    "chars": 4504,
    "preview": "package typedapi.server\n\nimport typedapi.shared.MethodType\nimport cats.Monad\nimport cats.implicits._\nimport cats.effect."
  },
  {
    "path": "js-client/src/main/scala/typedapi/client/js/package.scala",
    "chars": 6130,
    "preview": "package typedapi.client\n\nimport typedapi.util._\nimport org.scalajs.dom.XMLHttpRequest\nimport org.scalajs.dom.ext.Ajax\n\ni"
  },
  {
    "path": "project/build.properties",
    "chars": 17,
    "preview": "sbt.version=1.0.4"
  },
  {
    "path": "project/build.scala",
    "chars": 2018,
    "preview": "import sbt._\n\nobject Dependencies {\n\n  private val specs2V = \"3.9.4\"\n\n  val shared = Seq(\n    \"com.chuusai\" %% \"shapeles"
  },
  {
    "path": "project/plugins.sbt",
    "chars": 165,
    "preview": "addSbtPlugin(\"org.xerial.sbt\" % \"sbt-sonatype\" % \"2.3\")\n\naddSbtPlugin(\"com.jsuereth\" % \"sbt-pgp\" % \"1.1.0\")\n\naddSbtPlugi"
  },
  {
    "path": "scalaj-http-client/src/main/scala/typedapi/client/scalajhttp/package.scala",
    "chars": 5086,
    "preview": "package typedapi.client\n\nimport typedapi.util._\nimport scalaj.http._\n\npackage object scalajhttp {\n\n  type Id[A]       = "
  },
  {
    "path": "server/src/main/scala/typedapi/server/Endpoint.scala",
    "chars": 2520,
    "preview": "package typedapi.server\n\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.labelled.FieldType\nimport shapeles"
  },
  {
    "path": "server/src/main/scala/typedapi/server/EndpointComposition.scala",
    "chars": 5521,
    "preview": "package typedapi.server\n\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.labelled.FieldType\nimport shapeles"
  },
  {
    "path": "server/src/main/scala/typedapi/server/EndpointExecutor.scala",
    "chars": 1756,
    "preview": "package typedapi.server\n\nimport typedapi.shared.MethodType\nimport shapeless._\nimport shapeless.ops.hlist.Prepend\n\nimport"
  },
  {
    "path": "server/src/main/scala/typedapi/server/FilterClientElements.scala",
    "chars": 1740,
    "preview": "package typedapi.server\n\nimport typedapi.shared.{ClientHeaderElement, ClientHeaderParam, ClientHeaderCollParam}\nimport s"
  },
  {
    "path": "server/src/main/scala/typedapi/server/RouteExtractor.scala",
    "chars": 12984,
    "preview": "package typedapi.server\n\nimport typedapi.shared._\nimport shapeless.{HList, HNil, Witness}\nimport shapeless.labelled.Fiel"
  },
  {
    "path": "server/src/main/scala/typedapi/server/Serve.scala",
    "chars": 299,
    "preview": "package typedapi.server\n\n/** Reduces an Endpoint and its EndpointExecutor to a simple Request => Response function. */\nt"
  },
  {
    "path": "server/src/main/scala/typedapi/server/ServeToList.scala",
    "chars": 825,
    "preview": "package typedapi.server\n\nimport shapeless._\n\nimport scala.annotation.implicitNotFound\n\n@implicitNotFound(\"\"\"Cannot find "
  },
  {
    "path": "server/src/main/scala/typedapi/server/ServerHeaderExtractor.scala",
    "chars": 1267,
    "preview": "package typedapi.server\n\nimport typedapi.shared._\nimport shapeless._\n\nimport scala.annotation.implicitNotFound\n\n@implici"
  },
  {
    "path": "server/src/main/scala/typedapi/server/ServerManager.scala",
    "chars": 1286,
    "preview": "package typedapi.server\n\nimport scala.annotation.tailrec\n\nfinal case class ServerManager[S](server: S, host: String, por"
  },
  {
    "path": "server/src/main/scala/typedapi/server/StatusCodes.scala",
    "chars": 3940,
    "preview": "package typedapi.server\n\nfinal case class SuccessCode(statusCode: Int) extends AnyVal\nfinal case class ErrorCode(statusC"
  },
  {
    "path": "server/src/main/scala/typedapi/server/package.scala",
    "chars": 2691,
    "preview": "package typedapi\n\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.ops.hlist.Mapper\n\nimport scala.language.h"
  },
  {
    "path": "server/src/test/scala/typedapi/server/ApiToEndpointLinkSpec.scala",
    "chars": 879,
    "preview": "package typedapi.server\n\nimport typedapi.dsl._\nimport shapeless._\n\nimport org.specs2.mutable.Specification\n\nfinal class "
  },
  {
    "path": "server/src/test/scala/typedapi/server/RouteExtractorSpec.scala",
    "chars": 7690,
    "preview": "package typedapi.server\n\nimport typedapi.dsl._\nimport typedapi.shared._\nimport shapeless.{HList, HNil, Lazy}\nimport org."
  },
  {
    "path": "server/src/test/scala/typedapi/server/ServeAndMountSpec.scala",
    "chars": 5703,
    "preview": "package typedapi.server\n\nimport typedapi.dsl._\nimport typedapi.shared.MethodType\nimport shapeless.{HList, HNil, ::}\nimpo"
  },
  {
    "path": "shared/src/main/scala/typedapi/dsl/ApiDsl.scala",
    "chars": 3586,
    "preview": "package typedapi.dsl\n\nimport typedapi.shared._\nimport shapeless._\n\nsealed trait ApiList[H <: HList]\n\n/** Basic operation"
  },
  {
    "path": "shared/src/main/scala/typedapi/dsl/package.scala",
    "chars": 1304,
    "preview": "package typedapi\n\nimport typedapi.shared._\n\npackage object dsl extends MethodToReqBodyLowPrio with MethodToStringLowPrio"
  },
  {
    "path": "shared/src/main/scala/typedapi/package.scala",
    "chars": 1793,
    "preview": "\nimport typedapi.shared._\nimport shapeless._\nimport shapeless.ops.hlist.Prepend\n\npackage object typedapi extends MethodT"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/ApiElement.scala",
    "chars": 3538,
    "preview": "package typedapi.shared\n\nimport scala.annotation.implicitNotFound\n\nsealed trait ApiElement\n\n/** Type-container providing"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/ApiList.scala",
    "chars": 2184,
    "preview": "package typedapi.shared\n\nimport shapeless._\n\n/** Typecarrier to construct a complete path description from [[PathElement"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/ApiTransformer.scala",
    "chars": 5589,
    "preview": "package typedapi.shared\n\nimport shapeless._\nimport shapeless.labelled.FieldType\n\nimport scala.annotation.implicitNotFoun"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/MediaTypes.scala",
    "chars": 32037,
    "preview": "package typedapi.shared\n\ntrait MediaType { self =>\n  def value: String\n}\n\nobject MediaTypes {\n\n  case object NoMediaType"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/TypeCarrier.scala",
    "chars": 1031,
    "preview": "package typedapi.shared\n\nimport shapeless._\n\nimport scala.language.higherKinds\n\n/** As the name says this case class is "
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/TypeLevelFoldLeft.scala",
    "chars": 2340,
    "preview": "package typedapi.shared\n\nimport shapeless._\n\nimport scala.annotation.implicitNotFound\n\n// INTERNAL API\n\n/** Reimplements"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/WitnessToString.scala",
    "chars": 458,
    "preview": "package typedapi.shared\n\nimport scala.annotation.implicitNotFound\n\n@implicitNotFound(\"Couldn't find transformation for w"
  },
  {
    "path": "shared/src/main/scala/typedapi/shared/package.scala",
    "chars": 167,
    "preview": "package typedapi\n\nimport shapeless.Witness\n\npackage object shared {\n\n  final val BodyField       = Witness('body)\n  fina"
  },
  {
    "path": "shared/src/main/scala/typedapi/util/Decoder.scala",
    "chars": 319,
    "preview": "package typedapi.util\n\nimport scala.language.higherKinds\n\ntrait Decoder[F[_], A] extends (String => F[Either[Exception, "
  },
  {
    "path": "shared/src/main/scala/typedapi/util/Encoder.scala",
    "chars": 258,
    "preview": "package typedapi.util\n\nimport scala.language.higherKinds\n\ntrait Encoder[F[_], A] extends (A => F[String])\n\nobject Encode"
  },
  {
    "path": "shared/src/test/scala/typedapi/ApiDefinitionSpec.scala",
    "chars": 2908,
    "preview": "package typedapi\n\nimport typedapi.shared._\nimport shapeless._\n\nimport SpecUtil._\n\n// compilation-only test\nobject ApiDef"
  },
  {
    "path": "shared/src/test/scala/typedapi/SpecUtil.scala",
    "chars": 283,
    "preview": "package typedapi\n\nimport shapeless.HList\n\nimport scala.language.higherKinds\n\nobject SpecUtil {\n\n  class TestHelper[Act <"
  },
  {
    "path": "shared/src/test/scala/typedapi/dsl/ApiDslSpec.scala",
    "chars": 3692,
    "preview": "package typedapi.dsl\n\nimport typedapi.SpecUtil._\nimport typedapi.shared._\nimport shapeless._\n\n// compilation-only test\no"
  },
  {
    "path": "shared/src/test/scala/typedapi/shared/ApiTransformerSpec.scala",
    "chars": 3359,
    "preview": "package typedapi.shared\n\nimport typedapi.Json\nimport shapeless._\nimport shapeless.labelled.FieldType\n\n// compilation-onl"
  }
]

About this extraction

This page contains the full source code of the pheymann/typedapi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 81 files (247.9 KB), approximately 71.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!