aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/scala/com/softwaremill/sttp/ResponseAs.scala
blob: 2bf4c35eb179c5ae72449ee3017563615fbf5fc1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.softwaremill.sttp

import java.io.{File, FileOutputStream, IOException, InputStream}
import java.net.URLDecoder

import scala.collection.immutable.Seq
import scala.language.higherKinds
import scala.util.Try

/**
  * @tparam T Target type as which the response will be read.
  * @tparam S If `T` is a stream, the type of the stream. Otherwise, `Nothing`.
  */
sealed trait ResponseAs[T, +S] {
  def map[T2](f: T => T2): ResponseAs[T2, S]
}

/**
  * Response handling specification which isn't derived from another response
  * handling method, but needs to be handled directly by the backend.
  */
sealed trait BasicResponseAs[T, +S] extends ResponseAs[T, S] {
  override def map[T2](f: (T) => T2): ResponseAs[T2, S] =
    MappedResponseAs[T, T2, S](this, f)
}

case object IgnoreResponse extends BasicResponseAs[Unit, Nothing]
case class ResponseAsString(encoding: String)
    extends BasicResponseAs[String, Nothing]
case object ResponseAsByteArray extends BasicResponseAs[Array[Byte], Nothing]
case class ResponseAsStream[T, S]()(implicit val responseIsStream: S =:= T)
    extends BasicResponseAs[T, S]

case class MappedResponseAs[T, T2, S](raw: BasicResponseAs[T, S], g: T => T2)
    extends ResponseAs[T2, S] {
  override def map[T3](f: T2 => T3): ResponseAs[T3, S] =
    MappedResponseAs[T, T3, S](raw, g andThen f)
}

case class ResponseAsFile(output: File, overwrite: Boolean)
    extends BasicResponseAs[File, Nothing]

object ResponseAs {
  private[sttp] def parseParams(s: String,
                                encoding: String): Seq[(String, String)] = {
    s.split("&")
      .toList
      .flatMap(kv =>
        kv.split("=", 2) match {
          case Array(k, v) =>
            Some(
              (URLDecoder.decode(k, encoding), URLDecoder.decode(v, encoding)))
          case _ => None
      })
  }

  private[sttp] def saveFile(file: File,
                             is: InputStream,
                             overwrite: Boolean): File = {
    if (!file.exists()) {
      file.getParentFile.mkdirs()
      file.createNewFile()
    } else if (!overwrite) {
      throw new IOException(
        s"File ${file.getAbsolutePath} exists - overwriting prohibited")
    }

    val os = new FileOutputStream(file)

    transfer(is, os)
    file
  }

  /**
    * Handles responses according to the given specification when basic
    * response specifications can be handled eagerly, that is without
    * wrapping the result in the target monad (`handleBasic` returns
    * `Try[T]`, not `R[T]`).
    */
  private[sttp] trait EagerResponseHandler[S] {
    def handleBasic[T](bra: BasicResponseAs[T, S]): Try[T]

    def handle[T, R[_]](responseAs: ResponseAs[T, S],
                        responseMonad: MonadError[R]): R[T] = {

      responseAs match {
        case mra @ MappedResponseAs(raw, g) =>
          responseMonad.map(responseMonad.fromTry(handleBasic(mra.raw)))(mra.g)
        case bra: BasicResponseAs[T, S] =>
          responseMonad.fromTry(handleBasic(bra))
      }
    }
  }
}